Mercurial > hg
annotate mcabber/connwrap/connwrap.c @ 430:d03663d2e7d9
Display error messages as specified in RFC3920 (9.3)
If possible, we display the child element corresponding to the stanza
error conditions defined in RFC3920. Error code and error text will
be displayed if available.
author | Mikael Berthe <mikael@lilotux.net> |
---|---|
date | Sun, 11 Sep 2005 22:01:57 +0200 |
parents | ac85ce87f539 |
children | 89aeb8fdd215 |
rev | line source |
---|---|
25 | 1 #include "connwrap.h" |
2 | |
302
8ca708a0d550
Remove compilation warnings in connwrap library
Mikael Berthe <mikael@lilotux.net>
parents:
235
diff
changeset
|
3 #include <stdio.h> |
8ca708a0d550
Remove compilation warnings in connwrap library
Mikael Berthe <mikael@lilotux.net>
parents:
235
diff
changeset
|
4 #include <stdlib.h> |
25 | 5 #include <netdb.h> |
6 #include <string.h> | |
7 #include <netinet/in.h> | |
8 #include <errno.h> | |
9 #include <arpa/inet.h> | |
10 #include <fcntl.h> | |
11 #include <sys/time.h> | |
112 | 12 #include <unistd.h> |
25 | 13 |
14 #define PROXY_TIMEOUT 10 | |
15 // HTTP proxy timeout in seconds (for the CONNECT method) | |
16 | |
17 #ifdef HAVE_OPENSSL | |
18 | |
19 #define OPENSSL_NO_KRB5 1 | |
20 #include <openssl/ssl.h> | |
21 #include <openssl/err.h> | |
22 | |
134 | 23 #else |
24 # ifdef HAVE_GNUTLS | |
25 # include <gnutls/openssl.h> | |
26 # define HAVE_OPENSSL | |
27 # endif | |
25 | 28 #endif |
29 | |
30 static int in_http_connect = 0; | |
31 | |
32 #ifdef HAVE_OPENSSL | |
33 | |
34 static SSL_CTX *ctx = 0; | |
35 | |
36 typedef struct { int fd; SSL *ssl; } sslsock; | |
37 | |
38 static sslsock *socks = 0; | |
39 static int sockcount = 0; | |
40 | |
41 static sslsock *getsock(int fd) { | |
42 int i; | |
43 | |
44 for(i = 0; i < sockcount; i++) | |
45 if(socks[i].fd == fd) | |
46 return &socks[i]; | |
47 | |
48 return 0; | |
49 } | |
50 | |
51 static sslsock *addsock(int fd) { | |
52 sslsock *p; | |
53 socks = (sslsock *) realloc(socks, sizeof(sslsock)*++sockcount); | |
54 | |
55 p = &socks[sockcount-1]; | |
56 | |
57 if(!ctx) { | |
58 SSL_library_init(); | |
59 SSL_load_error_strings(); | |
60 | |
61 #ifdef HAVE_SSLEAY | |
62 SSLeay_add_all_algorithms(); | |
63 #else | |
64 OpenSSL_add_all_algorithms(); | |
65 #endif | |
66 | |
134 | 67 //ctx = SSL_CTX_new(SSLv23_method()); |
68 ctx = SSL_CTX_new(SSLv23_client_method()); | |
25 | 69 } |
70 | |
71 p->ssl = SSL_new(ctx); | |
72 SSL_set_fd(p->ssl, p->fd = fd); | |
73 | |
74 return p; | |
75 } | |
76 | |
77 static void delsock(int fd) { | |
78 int i, nsockcount; | |
79 sslsock *nsocks; | |
80 | |
81 nsockcount = 0; | |
82 nsocks = (sslsock *) malloc(sizeof(sslsock)*(sockcount-1)); | |
83 | |
84 for(i = 0; i < sockcount; i++) { | |
85 if(socks[i].fd != fd) { | |
86 nsocks[nsockcount++] = socks[i]; | |
87 } else { | |
88 SSL_free(socks[i].ssl); | |
89 } | |
90 } | |
91 | |
92 free(socks); | |
93 | |
94 socks = nsocks; | |
95 sockcount = nsockcount; | |
96 } | |
97 | |
98 #endif | |
99 | |
100 static char *bindaddr = 0, *proxyhost = 0, *proxyuser = 0, *proxypass = 0; | |
101 static int proxyport = 3128; | |
102 static int proxy_ssl = 0; | |
103 | |
104 #define SOCKOUT(s) write(sockfd, s, strlen(s)) | |
105 | |
106 int cw_http_connect(int sockfd, const struct sockaddr *serv_addr, int addrlen) { | |
107 int err, pos, fl; | |
108 struct hostent *server; | |
109 struct sockaddr_in paddr; | |
110 char buf[512]; | |
111 fd_set rfds; | |
112 | |
400
e536ab271584
Kill a warning in the connwrap library
Mikael Berthe <mikael@lilotux.net>
parents:
302
diff
changeset
|
113 fl = 0; |
25 | 114 err = 0; |
115 in_http_connect = 1; | |
116 | |
117 if(!(server = gethostbyname(proxyhost))) { | |
118 errno = h_errno; | |
119 err = -1; | |
120 } | |
121 | |
122 if(!err) { | |
123 memset(&paddr, 0, sizeof(paddr)); | |
124 paddr.sin_family = AF_INET; | |
125 memcpy(&paddr.sin_addr.s_addr, *server->h_addr_list, server->h_length); | |
126 paddr.sin_port = htons(proxyport); | |
127 | |
128 fl = fcntl(sockfd, F_GETFL); | |
129 fcntl(sockfd, F_SETFL, fl & ~O_NONBLOCK); | |
130 | |
131 buf[0] = 0; | |
132 | |
133 err = cw_connect(sockfd, (struct sockaddr *) &paddr, sizeof(paddr), proxy_ssl); | |
134 } | |
135 | |
136 errno = ECONNREFUSED; | |
137 | |
138 if(!err) { | |
139 struct sockaddr_in *sin = (struct sockaddr_in *) serv_addr; | |
140 char *ip = inet_ntoa(sin->sin_addr), c; | |
141 struct timeval tv; | |
142 | |
143 sprintf(buf, "%d", ntohs(sin->sin_port)); | |
144 SOCKOUT("CONNECT "); | |
145 SOCKOUT(ip); | |
146 SOCKOUT(":"); | |
147 SOCKOUT(buf); | |
148 SOCKOUT(" HTTP/1.0\r\n"); | |
149 | |
150 if(proxyuser) { | |
151 char *b; | |
152 SOCKOUT("Proxy-Authorization: Basic "); | |
153 | |
427
ac85ce87f539
Fix buffer overflow in cw_setproxy()
Mikael Berthe <mikael@lilotux.net>
parents:
414
diff
changeset
|
154 snprintf(buf, sizeof(buf), "%s:%s", proxyuser, proxypass); |
25 | 155 b = cw_base64_encode(buf); |
156 SOCKOUT(b); | |
157 free(b); | |
158 | |
159 SOCKOUT("\r\n"); | |
160 } | |
161 | |
162 SOCKOUT("\r\n"); | |
163 | |
164 buf[0] = 0; | |
165 | |
166 while(err != -1) { | |
167 FD_ZERO(&rfds); | |
168 FD_SET(sockfd, &rfds); | |
169 | |
170 tv.tv_sec = PROXY_TIMEOUT; | |
171 tv.tv_usec = 0; | |
172 | |
173 err = select(sockfd+1, &rfds, 0, 0, &tv); | |
174 | |
175 if(err < 1) err = -1; | |
176 | |
177 if(err != -1 && FD_ISSET(sockfd, &rfds)) { | |
178 err = read(sockfd, &c, 1); | |
179 if(!err) err = -1; | |
180 | |
181 if(err != -1) { | |
182 pos = strlen(buf); | |
183 buf[pos] = c; | |
184 buf[pos+1] = 0; | |
185 | |
186 if(strlen(buf) > 4) | |
187 if(!strcmp(buf+strlen(buf)-4, "\r\n\r\n")) | |
188 break; | |
189 } | |
190 } | |
191 } | |
192 } | |
193 | |
194 if(err != -1 && strlen(buf)) { | |
195 char *p = strstr(buf, " "); | |
196 | |
197 err = -1; | |
198 | |
199 if(p) | |
200 if(atoi(++p) == 200) | |
201 err = 0; | |
202 | |
203 fcntl(sockfd, F_SETFL, fl); | |
204 if(fl & O_NONBLOCK) { | |
205 errno = EINPROGRESS; | |
206 err = -1; | |
207 } | |
208 } | |
209 | |
210 in_http_connect = 0; | |
211 | |
212 return err; | |
213 } | |
214 | |
215 int cw_connect(int sockfd, const struct sockaddr *serv_addr, int addrlen, int ssl) { | |
216 int rc; | |
217 struct sockaddr_in ba; | |
218 | |
219 if(bindaddr) | |
220 if(strlen(bindaddr)) { | |
221 #ifdef HAVE_INET_ATON | |
222 struct in_addr addr; | |
223 rc = inet_aton(bindaddr, &addr); | |
224 ba.sin_addr.s_addr = addr.s_addr; | |
225 #else | |
226 rc = inet_pton(AF_INET, bindaddr, &ba); | |
227 #endif | |
228 | |
229 if(rc) { | |
230 ba.sin_port = 0; | |
231 rc = bind(sockfd, (struct sockaddr *) &ba, sizeof(ba)); | |
232 } else { | |
233 rc = -1; | |
234 } | |
235 | |
236 if(rc) return rc; | |
237 } | |
238 | |
239 if(proxyhost && !in_http_connect) rc = cw_http_connect(sockfd, serv_addr, addrlen); | |
240 else rc = connect(sockfd, serv_addr, addrlen); | |
241 | |
242 #ifdef HAVE_OPENSSL | |
243 if(ssl && !rc) { | |
244 sslsock *p = addsock(sockfd); | |
245 if(SSL_connect(p->ssl) != 1) | |
246 return -1; | |
247 } | |
248 #endif | |
249 | |
250 return rc; | |
251 } | |
252 | |
253 int cw_nb_connect(int sockfd, const struct sockaddr *serv_addr, int addrlen, int ssl, int *state) { | |
254 int rc = 0; | |
255 struct sockaddr_in ba; | |
256 | |
257 if(bindaddr) | |
258 if(strlen(bindaddr)) { | |
259 #ifdef HAVE_INET_ATON | |
260 struct in_addr addr; | |
261 rc = inet_aton(bindaddr, &addr); | |
262 ba.sin_addr.s_addr = addr.s_addr; | |
263 #else | |
264 rc = inet_pton(AF_INET, bindaddr, &ba); | |
265 #endif | |
266 | |
267 if(rc) { | |
268 ba.sin_port = 0; | |
269 rc = bind(sockfd, (struct sockaddr *) &ba, sizeof(ba)); | |
270 } else { | |
271 rc = -1; | |
272 } | |
273 | |
274 if(rc) return rc; | |
275 } | |
276 | |
277 #ifdef HAVE_OPENSSL | |
278 if(ssl) { | |
279 if ( !(*state & CW_CONNECT_WANT_SOMETHING)) | |
280 rc = cw_connect(sockfd, serv_addr, addrlen, 0); | |
281 else{ /* check if the socket is connected correctly */ | |
282 int optlen = sizeof(int), optval; | |
235 | 283 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, (socklen_t*)&optlen) || optval) |
25 | 284 return -1; |
285 } | |
286 | |
287 if(!rc) { | |
288 sslsock *p; | |
289 if (*state & CW_CONNECT_SSL) | |
290 p = getsock(sockfd); | |
291 else | |
292 p = addsock(sockfd); | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
409
diff
changeset
|
293 |
25 | 294 rc = SSL_connect(p->ssl); |
295 switch(rc){ | |
296 case 1: | |
297 *state = 0; | |
298 return 0; | |
299 case 0: | |
300 return -1; | |
301 default: | |
302 switch (SSL_get_error(p->ssl, rc)){ | |
303 case SSL_ERROR_WANT_READ: | |
304 *state = CW_CONNECT_SSL | CW_CONNECT_WANT_READ; | |
305 return 0; | |
306 case SSL_ERROR_WANT_WRITE: | |
307 *state = CW_CONNECT_SSL | CW_CONNECT_WANT_WRITE; | |
308 return 0; | |
309 default: | |
310 return -1; | |
311 } | |
312 } | |
313 } | |
314 else{ /* catch EINPROGRESS error from the connect call */ | |
315 if (errno == EINPROGRESS){ | |
316 *state = CW_CONNECT_STARTED | CW_CONNECT_WANT_WRITE; | |
317 return 0; | |
318 } | |
319 } | |
320 | |
321 return rc; | |
322 } | |
323 #endif | |
324 if ( !(*state & CW_CONNECT_WANT_SOMETHING)) | |
325 rc = connect(sockfd, serv_addr, addrlen); | |
326 else{ /* check if the socket is connected correctly */ | |
327 int optlen = sizeof(int), optval; | |
409
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
328 fd_set fds; |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
329 struct timeval tv; |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
330 |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
331 FD_ZERO(&fds); |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
332 FD_SET(sockfd, &fds); |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
333 |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
334 tv.tv_sec = 0; |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
335 tv.tv_usec = 10000; |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
336 |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
337 if (select(sockfd+1, 0, &fds, 0, &tv) < 1) |
82064124324d
Fix a SIGPIPE issue when connecting to the server
Mikael Berthe <mikael@lilotux.net>
parents:
400
diff
changeset
|
338 return -1; |
235 | 339 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, (socklen_t*)&optlen) || optval) |
25 | 340 return -1; |
341 *state = 0; | |
342 return 0; | |
343 } | |
344 if (rc) | |
345 if (errno == EINPROGRESS){ | |
346 *state = CW_CONNECT_STARTED | CW_CONNECT_WANT_WRITE; | |
347 return 0; | |
348 } | |
349 return rc; | |
350 } | |
351 | |
352 int cw_accept(int s, struct sockaddr *addr, int *addrlen, int ssl) { | |
353 #ifdef HAVE_OPENSSL | |
354 int rc; | |
355 | |
356 if(ssl) { | |
235 | 357 rc = accept(s, addr, (socklen_t*)addrlen); |
25 | 358 |
359 if(!rc) { | |
360 sslsock *p = addsock(s); | |
361 if(SSL_accept(p->ssl) != 1) | |
362 return -1; | |
363 | |
364 } | |
365 | |
366 return rc; | |
367 } | |
368 #endif | |
235 | 369 return accept(s, addr, (socklen_t*)addrlen); |
25 | 370 } |
371 | |
372 int cw_write(int fd, const void *buf, int count, int ssl) { | |
373 #ifdef HAVE_OPENSSL | |
374 sslsock *p; | |
375 | |
376 if(ssl) | |
235 | 377 if((p = getsock(fd)) != NULL) |
25 | 378 return SSL_write(p->ssl, buf, count); |
379 #endif | |
380 return write(fd, buf, count); | |
381 } | |
382 | |
383 int cw_read(int fd, void *buf, int count, int ssl) { | |
384 #ifdef HAVE_OPENSSL | |
385 sslsock *p; | |
386 | |
387 if(ssl) | |
235 | 388 if((p = getsock(fd)) != NULL) |
25 | 389 return SSL_read(p->ssl, buf, count); |
390 #endif | |
391 return read(fd, buf, count); | |
392 } | |
393 | |
235 | 394 void cw_close(int fd) { |
25 | 395 #ifdef HAVE_OPENSSL |
396 delsock(fd); | |
397 #endif | |
398 close(fd); | |
399 } | |
400 | |
401 #define FREEVAR(v) if(v) free(v), v = 0; | |
402 | |
403 void cw_setbind(const char *abindaddr) { | |
404 FREEVAR(bindaddr); | |
405 bindaddr = strdup(abindaddr); | |
406 } | |
407 | |
408 void cw_setproxy(const char *aproxyhost, int aproxyport, const char *aproxyuser, const char *aproxypass) { | |
409 FREEVAR(proxyhost); | |
410 FREEVAR(proxyuser); | |
411 FREEVAR(proxypass); | |
412 | |
413 if(aproxyhost && strlen(aproxyhost)) proxyhost = strdup(aproxyhost); | |
414 if(aproxyuser && strlen(aproxyuser)) proxyuser = strdup(aproxyuser); | |
415 if(aproxypass && strlen(aproxypass)) proxypass = strdup(aproxypass); | |
416 proxyport = aproxyport; | |
417 } | |
418 | |
419 char *cw_base64_encode(const char *in) { | |
420 static char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; | |
421 | |
422 int j = 0; | |
423 int inlen = strlen(in); | |
424 char *out = (char *) malloc(inlen*4+1), c; | |
425 | |
426 for(out[0] = 0; inlen >= 3; inlen -= 3) { | |
427 strncat(out, &base64digits[ in[j] >> 2 ], 1); | |
428 strncat(out, &base64digits[ ((in[j] << 4) & 0x30) | (in[j+1] >> 4) ], 1); | |
429 strncat(out, &base64digits[ ((in[j+1] << 2) & 0x3c) | (in[j+2] >> 6) ], 1); | |
430 strncat(out, &base64digits[ in[j+2] & 0x3f ], 1); | |
431 j += 3; | |
432 } | |
433 | |
434 if(inlen > 0) { | |
435 unsigned char fragment; | |
436 | |
437 strncat(out, &base64digits[in[j] >> 2], 1); | |
438 fragment = (in[j] << 4) & 0x30; | |
439 | |
440 if(inlen > 1) | |
441 fragment |= in[j+1] >> 4; | |
442 | |
443 strncat(out, &base64digits[fragment], 1); | |
444 | |
445 c = (inlen < 2) ? '-' : base64digits[ (in[j+1] << 2) & 0x3c ]; | |
446 strncat(out, &c, 1); | |
447 c = '-'; | |
448 strncat(out, &c, 1); | |
449 } | |
414
ec86d759ed54
Trailing whitespace cleanup
Mikael Berthe <mikael@lilotux.net>
parents:
409
diff
changeset
|
450 |
25 | 451 return out; |
452 } |