s_socket.c revision 68651
1/* apps/s_socket.c - socket-related functions used by s_client and s_server */ 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay@cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh@cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay@cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <errno.h> 63#include <signal.h> 64 65/* With IPv6, it looks like Digital has mixed up the proper order of 66 recursive header file inclusion, resulting in the compiler complaining 67 that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which 68 is needed to have fileno() declared correctly... So let's define u_int */ 69#if defined(VMS) && defined(__DECC) && !defined(__U_INT) 70#define __U_INT 71typedef unsigned int u_int; 72#endif 73 74#define USE_SOCKETS 75#define NON_MAIN 76#include "apps.h" 77#undef USE_SOCKETS 78#undef NON_MAIN 79#include "s_apps.h" 80#include <openssl/ssl.h> 81 82static struct hostent *GetHostByName(char *name); 83#ifdef WINDOWS 84static void sock_cleanup(void); 85#endif 86static int sock_init(void); 87static int init_client_ip(int *sock,unsigned char ip[4], int port); 88static int init_server(int *sock, int port); 89static int init_server_long(int *sock, int port,char *ip); 90static int do_accept(int acc_sock, int *sock, char **host); 91static int host_ip(char *str, unsigned char ip[4]); 92 93#ifdef WIN16 94#define SOCKET_PROTOCOL 0 /* more microsoft stupidity */ 95#else 96#define SOCKET_PROTOCOL IPPROTO_TCP 97#endif 98 99#ifdef WINDOWS 100static struct WSAData wsa_state; 101static int wsa_init_done=0; 102 103#ifdef WIN16 104static HWND topWnd=0; 105static FARPROC lpTopWndProc=NULL; 106static FARPROC lpTopHookProc=NULL; 107extern HINSTANCE _hInstance; /* nice global CRT provides */ 108 109static LONG FAR PASCAL topHookProc(HWND hwnd, UINT message, WPARAM wParam, 110 LPARAM lParam) 111 { 112 if (hwnd == topWnd) 113 { 114 switch(message) 115 { 116 case WM_DESTROY: 117 case WM_CLOSE: 118 SetWindowLong(topWnd,GWL_WNDPROC,(LONG)lpTopWndProc); 119 sock_cleanup(); 120 break; 121 } 122 } 123 return CallWindowProc(lpTopWndProc,hwnd,message,wParam,lParam); 124 } 125 126static BOOL CALLBACK enumproc(HWND hwnd,LPARAM lParam) 127 { 128 topWnd=hwnd; 129 return(FALSE); 130 } 131 132#endif /* WIN32 */ 133#endif /* WINDOWS */ 134 135#ifdef WINDOWS 136static void sock_cleanup(void) 137 { 138 if (wsa_init_done) 139 { 140 wsa_init_done=0; 141 WSACancelBlockingCall(); 142 WSACleanup(); 143 } 144 } 145#endif 146 147static int sock_init(void) 148 { 149#ifdef WINDOWS 150 if (!wsa_init_done) 151 { 152 int err; 153 154#ifdef SIGINT 155 signal(SIGINT,(void (*)(int))sock_cleanup); 156#endif 157 wsa_init_done=1; 158 memset(&wsa_state,0,sizeof(wsa_state)); 159 if (WSAStartup(0x0101,&wsa_state)!=0) 160 { 161 err=WSAGetLastError(); 162 BIO_printf(bio_err,"unable to start WINSOCK, error code=%d\n",err); 163 return(0); 164 } 165 166#ifdef WIN16 167 EnumTaskWindows(GetCurrentTask(),enumproc,0L); 168 lpTopWndProc=(FARPROC)GetWindowLong(topWnd,GWL_WNDPROC); 169 lpTopHookProc=MakeProcInstance((FARPROC)topHookProc,_hInstance); 170 171 SetWindowLong(topWnd,GWL_WNDPROC,(LONG)lpTopHookProc); 172#endif /* WIN16 */ 173 } 174#endif /* WINDOWS */ 175 return(1); 176 } 177 178int init_client(int *sock, char *host, int port) 179 { 180 unsigned char ip[4]; 181 short p=0; 182 183 if (!host_ip(host,&(ip[0]))) 184 { 185 return(0); 186 } 187 if (p != 0) port=p; 188 return(init_client_ip(sock,ip,port)); 189 } 190 191static int init_client_ip(int *sock, unsigned char ip[4], int port) 192 { 193 unsigned long addr; 194 struct sockaddr_in them; 195 int s,i; 196 197 if (!sock_init()) return(0); 198 199 memset((char *)&them,0,sizeof(them)); 200 them.sin_family=AF_INET; 201 them.sin_port=htons((unsigned short)port); 202 addr=(unsigned long) 203 ((unsigned long)ip[0]<<24L)| 204 ((unsigned long)ip[1]<<16L)| 205 ((unsigned long)ip[2]<< 8L)| 206 ((unsigned long)ip[3]); 207 them.sin_addr.s_addr=htonl(addr); 208 209 s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL); 210 if (s == INVALID_SOCKET) { perror("socket"); return(0); } 211 212#ifndef MPE 213 i=0; 214 i=setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i)); 215 if (i < 0) { perror("keepalive"); return(0); } 216#endif 217 218 if (connect(s,(struct sockaddr *)&them,sizeof(them)) == -1) 219 { close(s); perror("connect"); return(0); } 220 *sock=s; 221 return(1); 222 } 223 224int do_server(int port, int *ret, int (*cb)(), char *context) 225 { 226 int sock; 227 char *name; 228 int accept_socket; 229 int i; 230 231 if (!init_server(&accept_socket,port)) return(0); 232 233 if (ret != NULL) 234 { 235 *ret=accept_socket; 236 /* return(1);*/ 237 } 238 for (;;) 239 { 240 if (do_accept(accept_socket,&sock,&name) == 0) 241 { 242 SHUTDOWN(accept_socket); 243 return(0); 244 } 245 i=(*cb)(name,sock, context); 246 if (name != NULL) OPENSSL_free(name); 247 SHUTDOWN2(sock); 248 if (i < 0) 249 { 250 SHUTDOWN2(accept_socket); 251 return(i); 252 } 253 } 254 } 255 256static int init_server_long(int *sock, int port, char *ip) 257 { 258 int ret=0; 259 struct sockaddr_in server; 260 int s= -1,i; 261 262 if (!sock_init()) return(0); 263 264 memset((char *)&server,0,sizeof(server)); 265 server.sin_family=AF_INET; 266 server.sin_port=htons((unsigned short)port); 267 if (ip == NULL) 268 server.sin_addr.s_addr=INADDR_ANY; 269 else 270/* Added for T3E, address-of fails on bit field (beckman@acl.lanl.gov) */ 271#ifndef BIT_FIELD_LIMITS 272 memcpy(&server.sin_addr.s_addr,ip,4); 273#else 274 memcpy(&server.sin_addr,ip,4); 275#endif 276 s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL); 277 278 if (s == INVALID_SOCKET) goto err; 279#if defined SOL_SOCKET && defined SO_REUSEADDR 280 { 281 int j = 1; 282 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 283 (void *) &j, sizeof j); 284 } 285#endif 286 if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1) 287 { 288#ifndef WINDOWS 289 perror("bind"); 290#endif 291 goto err; 292 } 293 /* Make it 128 for linux */ 294 if (listen(s,128) == -1) goto err; 295 i=0; 296 *sock=s; 297 ret=1; 298err: 299 if ((ret == 0) && (s != -1)) 300 { 301 SHUTDOWN(s); 302 } 303 return(ret); 304 } 305 306static int init_server(int *sock, int port) 307 { 308 return(init_server_long(sock, port, NULL)); 309 } 310 311static int do_accept(int acc_sock, int *sock, char **host) 312 { 313 int ret,i; 314 struct hostent *h1,*h2; 315 static struct sockaddr_in from; 316 int len; 317/* struct linger ling; */ 318 319 if (!sock_init()) return(0); 320 321#ifndef WINDOWS 322redoit: 323#endif 324 325 memset((char *)&from,0,sizeof(from)); 326 len=sizeof(from); 327 /* Note: under VMS with SOCKETSHR the fourth parameter is currently 328 * of type (int *) whereas under other systems it is (void *) if 329 * you don't have a cast it will choke the compiler: if you do 330 * have a cast then you can either go for (int *) or (void *). 331 */ 332 ret=accept(acc_sock,(struct sockaddr *)&from,(void *)&len); 333 if (ret == INVALID_SOCKET) 334 { 335#ifdef WINDOWS 336 i=WSAGetLastError(); 337 BIO_printf(bio_err,"accept error %d\n",i); 338#else 339 if (errno == EINTR) 340 { 341 /*check_timeout(); */ 342 goto redoit; 343 } 344 fprintf(stderr,"errno=%d ",errno); 345 perror("accept"); 346#endif 347 return(0); 348 } 349 350/* 351 ling.l_onoff=1; 352 ling.l_linger=0; 353 i=setsockopt(ret,SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling)); 354 if (i < 0) { perror("linger"); return(0); } 355 i=0; 356 i=setsockopt(ret,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i)); 357 if (i < 0) { perror("keepalive"); return(0); } 358*/ 359 360 if (host == NULL) goto end; 361#ifndef BIT_FIELD_LIMITS 362 /* I should use WSAAsyncGetHostByName() under windows */ 363 h1=gethostbyaddr((char *)&from.sin_addr.s_addr, 364 sizeof(from.sin_addr.s_addr),AF_INET); 365#else 366 h1=gethostbyaddr((char *)&from.sin_addr, 367 sizeof(struct in_addr),AF_INET); 368#endif 369 if (h1 == NULL) 370 { 371 BIO_printf(bio_err,"bad gethostbyaddr\n"); 372 *host=NULL; 373 /* return(0); */ 374 } 375 else 376 { 377 if ((*host=(char *)OPENSSL_malloc(strlen(h1->h_name)+1)) == NULL) 378 { 379 perror("OPENSSL_malloc"); 380 return(0); 381 } 382 strcpy(*host,h1->h_name); 383 384 h2=GetHostByName(*host); 385 if (h2 == NULL) 386 { 387 BIO_printf(bio_err,"gethostbyname failure\n"); 388 return(0); 389 } 390 i=0; 391 if (h2->h_addrtype != AF_INET) 392 { 393 BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n"); 394 return(0); 395 } 396 } 397end: 398 *sock=ret; 399 return(1); 400 } 401 402int extract_host_port(char *str, char **host_ptr, unsigned char *ip, 403 short *port_ptr) 404 { 405 char *h,*p; 406 407 h=str; 408 p=strchr(str,':'); 409 if (p == NULL) 410 { 411 BIO_printf(bio_err,"no port defined\n"); 412 return(0); 413 } 414 *(p++)='\0'; 415 416 if ((ip != NULL) && !host_ip(str,ip)) 417 goto err; 418 if (host_ptr != NULL) *host_ptr=h; 419 420 if (!extract_port(p,port_ptr)) 421 goto err; 422 return(1); 423err: 424 return(0); 425 } 426 427static int host_ip(char *str, unsigned char ip[4]) 428 { 429 unsigned int in[4]; 430 int i; 431 432 if (sscanf(str,"%u.%u.%u.%u",&(in[0]),&(in[1]),&(in[2]),&(in[3])) == 4) 433 { 434 for (i=0; i<4; i++) 435 if (in[i] > 255) 436 { 437 BIO_printf(bio_err,"invalid IP address\n"); 438 goto err; 439 } 440 ip[0]=in[0]; 441 ip[1]=in[1]; 442 ip[2]=in[2]; 443 ip[3]=in[3]; 444 } 445 else 446 { /* do a gethostbyname */ 447 struct hostent *he; 448 449 if (!sock_init()) return(0); 450 451 he=GetHostByName(str); 452 if (he == NULL) 453 { 454 BIO_printf(bio_err,"gethostbyname failure\n"); 455 goto err; 456 } 457 /* cast to short because of win16 winsock definition */ 458 if ((short)he->h_addrtype != AF_INET) 459 { 460 BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n"); 461 return(0); 462 } 463 ip[0]=he->h_addr_list[0][0]; 464 ip[1]=he->h_addr_list[0][1]; 465 ip[2]=he->h_addr_list[0][2]; 466 ip[3]=he->h_addr_list[0][3]; 467 } 468 return(1); 469err: 470 return(0); 471 } 472 473int extract_port(char *str, short *port_ptr) 474 { 475 int i; 476 struct servent *s; 477 478 i=atoi(str); 479 if (i != 0) 480 *port_ptr=(unsigned short)i; 481 else 482 { 483 s=getservbyname(str,"tcp"); 484 if (s == NULL) 485 { 486 BIO_printf(bio_err,"getservbyname failure for %s\n",str); 487 return(0); 488 } 489 *port_ptr=ntohs((unsigned short)s->s_port); 490 } 491 return(1); 492 } 493 494#define GHBN_NUM 4 495static struct ghbn_cache_st 496 { 497 char name[128]; 498 struct hostent ent; 499 unsigned long order; 500 } ghbn_cache[GHBN_NUM]; 501 502static unsigned long ghbn_hits=0L; 503static unsigned long ghbn_miss=0L; 504 505static struct hostent *GetHostByName(char *name) 506 { 507 struct hostent *ret; 508 int i,lowi=0; 509 unsigned long low= (unsigned long)-1; 510 511 for (i=0; i<GHBN_NUM; i++) 512 { 513 if (low > ghbn_cache[i].order) 514 { 515 low=ghbn_cache[i].order; 516 lowi=i; 517 } 518 if (ghbn_cache[i].order > 0) 519 { 520 if (strncmp(name,ghbn_cache[i].name,128) == 0) 521 break; 522 } 523 } 524 if (i == GHBN_NUM) /* no hit*/ 525 { 526 ghbn_miss++; 527 ret=gethostbyname(name); 528 if (ret == NULL) return(NULL); 529 /* else add to cache */ 530 strncpy(ghbn_cache[lowi].name,name,128); 531 memcpy((char *)&(ghbn_cache[lowi].ent),ret,sizeof(struct hostent)); 532 ghbn_cache[lowi].order=ghbn_miss+ghbn_hits; 533 return(ret); 534 } 535 else 536 { 537 ghbn_hits++; 538 ret= &(ghbn_cache[i].ent); 539 ghbn_cache[i].order=ghbn_miss+ghbn_hits; 540 return(ret); 541 } 542 } 543