]> git.proxmox.com Git - libgit2.git/blame - src/socket_stream.c
treebuilder: fix memory leaks in `write_with_buffer`
[libgit2.git] / src / socket_stream.c
CommitLineData
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
31static 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
44static void net_set_error(const char *str)
45{
46 giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
47}
48#endif
49
50static 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
71int 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 134ssize_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
154ssize_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
165int 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
176void 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
185int 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}