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