2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
8 #include "streams/socket.h"
16 # include <sys/types.h>
17 # include <sys/socket.h>
18 # include <sys/select.h>
19 # include <sys/time.h>
21 # include <netinet/in.h>
22 # include <arpa/inet.h>
24 # include <winsock2.h>
25 # include <ws2tcpip.h>
27 # pragma comment(lib, "ws2_32")
32 static void net_set_error(const char *str
)
34 int error
= WSAGetLastError();
35 char * win32_error
= git_win32_get_error_message(error
);
38 git_error_set(GIT_ERROR_NET
, "%s: %s", str
, win32_error
);
39 git__free(win32_error
);
41 git_error_set(GIT_ERROR_NET
, "%s", str
);
45 static void net_set_error(const char *str
)
47 git_error_set(GIT_ERROR_NET
, "%s: %s", str
, strerror(errno
));
51 static int close_socket(GIT_SOCKET s
)
53 if (s
== INVALID_SOCKET
)
57 if (SOCKET_ERROR
== closesocket(s
))
60 if (0 != WSACleanup()) {
61 git_error_set(GIT_ERROR_OS
, "winsock cleanup failed");
72 static int socket_connect(git_stream
*stream
)
74 struct addrinfo
*info
= NULL
, *p
;
75 struct addrinfo hints
;
76 git_socket_stream
*st
= (git_socket_stream
*) stream
;
77 GIT_SOCKET s
= INVALID_SOCKET
;
81 /* on win32, the WSA context needs to be initialized
82 * before any socket calls can be performed */
85 if (WSAStartup(MAKEWORD(2,2), &wsd
) != 0) {
86 git_error_set(GIT_ERROR_OS
, "winsock init failed");
90 if (LOBYTE(wsd
.wVersion
) != 2 || HIBYTE(wsd
.wVersion
) != 2) {
92 git_error_set(GIT_ERROR_OS
, "winsock init failed");
97 memset(&hints
, 0x0, sizeof(struct addrinfo
));
98 hints
.ai_socktype
= SOCK_STREAM
;
99 hints
.ai_family
= AF_UNSPEC
;
101 if ((ret
= p_getaddrinfo(st
->host
, st
->port
, &hints
, &info
)) != 0) {
102 git_error_set(GIT_ERROR_NET
,
103 "failed to resolve address for %s: %s", st
->host
, p_gai_strerror(ret
));
107 for (p
= info
; p
!= NULL
; p
= p
->ai_next
) {
108 s
= socket(p
->ai_family
, p
->ai_socktype
| SOCK_CLOEXEC
, p
->ai_protocol
);
110 if (s
== INVALID_SOCKET
)
113 if (connect(s
, p
->ai_addr
, (socklen_t
)p
->ai_addrlen
) == 0)
116 /* If we can't connect, try the next one */
121 /* Oops, we couldn't connect to any address */
122 if (s
== INVALID_SOCKET
&& p
== NULL
) {
123 git_error_set(GIT_ERROR_OS
, "failed to connect to %s", st
->host
);
124 p_freeaddrinfo(info
);
129 p_freeaddrinfo(info
);
133 static ssize_t
socket_write(git_stream
*stream
, const char *data
, size_t len
, int flags
)
135 git_socket_stream
*st
= (git_socket_stream
*) stream
;
140 if ((written
= p_send(st
->s
, data
, len
, flags
)) < 0) {
141 net_set_error("error sending data");
148 static ssize_t
socket_read(git_stream
*stream
, void *data
, size_t len
)
151 git_socket_stream
*st
= (git_socket_stream
*) stream
;
153 if ((ret
= p_recv(st
->s
, data
, len
, 0)) < 0)
154 net_set_error("error receiving socket data");
159 static int socket_close(git_stream
*stream
)
161 git_socket_stream
*st
= (git_socket_stream
*) stream
;
164 error
= close_socket(st
->s
);
165 st
->s
= INVALID_SOCKET
;
170 static void socket_free(git_stream
*stream
)
172 git_socket_stream
*st
= (git_socket_stream
*) stream
;
179 static int default_socket_stream_new(
184 git_socket_stream
*st
;
186 assert(out
&& host
&& port
);
188 st
= git__calloc(1, sizeof(git_socket_stream
));
189 GIT_ERROR_CHECK_ALLOC(st
);
191 st
->host
= git__strdup(host
);
192 GIT_ERROR_CHECK_ALLOC(st
->host
);
195 st
->port
= git__strdup(port
);
196 GIT_ERROR_CHECK_ALLOC(st
->port
);
199 st
->parent
.version
= GIT_STREAM_VERSION
;
200 st
->parent
.connect
= socket_connect
;
201 st
->parent
.write
= socket_write
;
202 st
->parent
.read
= socket_read
;
203 st
->parent
.close
= socket_close
;
204 st
->parent
.free
= socket_free
;
205 st
->s
= INVALID_SOCKET
;
207 *out
= (git_stream
*) st
;
211 int git_socket_stream_new(
216 int (*init
)(git_stream
**, const char *, const char *) = NULL
;
217 git_stream_registration custom
= {0};
220 assert(out
&& host
&& port
);
222 if ((error
= git_stream_registry_lookup(&custom
, GIT_STREAM_STANDARD
)) == 0)
224 else if (error
== GIT_ENOTFOUND
)
225 init
= default_socket_stream_new
;
230 git_error_set(GIT_ERROR_NET
, "there is no socket stream available");
234 return init(out
, host
, port
);