Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
3 - *
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.
6 - */
7 -
8 - #include "streams/socket.h"
9 -
10 - #include "posix.h"
11 - #include "netops.h"
12 - #include "registry.h"
13 - #include "stream.h"
14 -
15 - #ifndef _WIN32
16 - # include <sys/types.h>
17 - # include <sys/socket.h>
18 - # include <sys/select.h>
19 - # include <sys/time.h>
20 - # include <netdb.h>
21 - # include <netinet/in.h>
22 - # include <arpa/inet.h>
23 - #else
24 - # include <winsock2.h>
25 - # include <ws2tcpip.h>
26 - # ifdef _MSC_VER
27 - # pragma comment(lib, "ws2_32")
28 - # endif
29 - #endif
30 -
31 - #ifdef GIT_WIN32
32 - static void net_set_error(const char *str)
33 - {
34 - int error = WSAGetLastError();
35 - char * win32_error = git_win32_get_error_message(error);
36 -
37 - if (win32_error) {
38 - git_error_set(GIT_ERROR_NET, "%s: %s", str, win32_error);
39 - git__free(win32_error);
40 - } else {
41 - git_error_set(GIT_ERROR_NET, "%s", str);
42 - }
43 - }
44 - #else
45 ##### 2 static void net_set_error(const char *str)
46 - {
47 ##### 2-4 git_error_set(GIT_ERROR_NET, "%s: %s", str, strerror(errno));
48 ##### 5 }
49 - #endif
50 -
51 92 2 static int close_socket(GIT_SOCKET s)
52 - {
53 92 2 if (s == INVALID_SOCKET)
54 ##### 3 return 0;
55 -
56 - #ifdef GIT_WIN32
57 - if (SOCKET_ERROR == closesocket(s))
58 - return -1;
59 -
60 - if (0 != WSACleanup()) {
61 - git_error_set(GIT_ERROR_OS, "winsock cleanup failed");
62 - return -1;
63 - }
64 -
65 - return 0;
66 - #else
67 92 4 return close(s);
68 - #endif
69 -
70 - }
71 -
72 92 2 static int socket_connect(git_stream *stream)
73 - {
74 92 2 struct addrinfo *info = NULL, *p;
75 - struct addrinfo hints;
76 92 2 git_socket_stream *st = (git_socket_stream *) stream;
77 92 2 GIT_SOCKET s = INVALID_SOCKET;
78 - int ret;
79 -
80 - #ifdef GIT_WIN32
81 - /* on win32, the WSA context needs to be initialized
82 - * before any socket calls can be performed */
83 - WSADATA wsd;
84 -
85 - if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
86 - git_error_set(GIT_ERROR_OS, "winsock init failed");
87 - return -1;
88 - }
89 -
90 - if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
91 - WSACleanup();
92 - git_error_set(GIT_ERROR_OS, "winsock init failed");
93 - return -1;
94 - }
95 - #endif
96 -
97 92 2 memset(&hints, 0x0, sizeof(struct addrinfo));
98 92 2 hints.ai_socktype = SOCK_STREAM;
99 92 2 hints.ai_family = AF_UNSPEC;
100 -
101 92 2,3 if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
102 ##### 4,5 git_error_set(GIT_ERROR_NET,
103 - "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
104 ##### 6 return -1;
105 - }
106 -
107 92 7,16,17 for (p = info; p != NULL; p = p->ai_next) {
108 92 8 s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
109 -
110 92 9 if (s == INVALID_SOCKET)
111 ##### 10 continue;
112 -
113 92 11,12 if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
114 92 13 break;
115 -
116 - /* If we can't connect, try the next one */
117 ##### 14 close_socket(s);
118 ##### 15 s = INVALID_SOCKET;
119 - }
120 -
121 - /* Oops, we couldn't connect to any address */
122 92 18,19 if (s == INVALID_SOCKET && p == NULL) {
123 ##### 20 git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
124 ##### 21 p_freeaddrinfo(info);
125 ##### 22 return -1;
126 - }
127 -
128 92 23 st->s = s;
129 92 23 p_freeaddrinfo(info);
130 92 24 return 0;
131 - }
132 -
133 316 2 static ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
134 - {
135 316 2 git_socket_stream *st = (git_socket_stream *) stream;
136 - ssize_t written;
137 -
138 316 2 errno = 0;
139 -
140 316 3,4 if ((written = p_send(st->s, data, len, flags)) < 0) {
141 ##### 5 net_set_error("error sending data");
142 ##### 6 return -1;
143 - }
144 -
145 316 7 return written;
146 - }
147 -
148 2158 2 static ssize_t socket_read(git_stream *stream, void *data, size_t len)
149 - {
150 - ssize_t ret;
151 2158 2 git_socket_stream *st = (git_socket_stream *) stream;
152 -
153 2158 2,3 if ((ret = p_recv(st->s, data, len, 0)) < 0)
154 ##### 4 net_set_error("error receiving socket data");
155 -
156 2158 5 return ret;
157 - }
158 -
159 92 2 static int socket_close(git_stream *stream)
160 - {
161 92 2 git_socket_stream *st = (git_socket_stream *) stream;
162 - int error;
163 -
164 92 2 error = close_socket(st->s);
165 92 3 st->s = INVALID_SOCKET;
166 -
167 92 3 return error;
168 - }
169 -
170 94 2 static void socket_free(git_stream *stream)
171 - {
172 94 2 git_socket_stream *st = (git_socket_stream *) stream;
173 -
174 94 2 git__free(st->host);
175 94 3 git__free(st->port);
176 94 4 git__free(st);
177 94 5 }
178 -
179 94 2 static int default_socket_stream_new(
180 - git_stream **out,
181 - const char *host,
182 - const char *port)
183 - {
184 - git_socket_stream *st;
185 -
186 94 2-5 assert(out && host && port);
187 -
188 94 6 st = git__calloc(1, sizeof(git_socket_stream));
189 94 7,8 GIT_ERROR_CHECK_ALLOC(st);
190 -
191 94 9 st->host = git__strdup(host);
192 94 10,11 GIT_ERROR_CHECK_ALLOC(st->host);
193 -
194 94 12 if (port) {
195 94 13 st->port = git__strdup(port);
196 94 14,15 GIT_ERROR_CHECK_ALLOC(st->port);
197 - }
198 -
199 94 16 st->parent.version = GIT_STREAM_VERSION;
200 94 16 st->parent.connect = socket_connect;
201 94 16 st->parent.write = socket_write;
202 94 16 st->parent.read = socket_read;
203 94 16 st->parent.close = socket_close;
204 94 16 st->parent.free = socket_free;
205 94 16 st->s = INVALID_SOCKET;
206 -
207 94 16 *out = (git_stream *) st;
208 94 16 return 0;
209 - }
210 -
211 96 2 int git_socket_stream_new(
212 - git_stream **out,
213 - const char *host,
214 - const char *port)
215 - {
216 96 2 int (*init)(git_stream **, const char *, const char *) = NULL;
217 96 2 git_stream_registration custom = {0};
218 - int error;
219 -
220 96 2-5 assert(out && host && port);
221 -
222 96 6,7 if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0)
223 2 8 init = custom.init;
224 94 9 else if (error == GIT_ENOTFOUND)
225 94 10 init = default_socket_stream_new;
226 - else
227 ##### 11 return error;
228 -
229 96 12 if (!init) {
230 ##### 13 git_error_set(GIT_ERROR_NET, "there is no socket stream available");
231 ##### 14 return -1;
232 - }
233 -
234 96 15 return init(out, host, port);
235 - }