source src/streams/socket.c
| 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 | - | } |