s_socket.c revision 59191
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 i=0; 213 i=setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i)); 214 if (i < 0) { perror("keepalive"); return(0); } 215 216 if (connect(s,(struct sockaddr *)&them,sizeof(them)) == -1) 217 { close(s); perror("connect"); return(0); } 218 *sock=s; 219 return(1); 220 } 221 222int do_server(int port, int *ret, int (*cb)(), char *context) 223 { 224 int sock; 225 char *name; 226 int accept_socket; 227 int i; 228 229 if (!init_server(&accept_socket,port)) return(0); 230 231 if (ret != NULL) 232 { 233 *ret=accept_socket; 234 /* return(1);*/ 235 } 236 for (;;) 237 { 238 if (do_accept(accept_socket,&sock,&name) == 0) 239 { 240 SHUTDOWN(accept_socket); 241 return(0); 242 } 243 i=(*cb)(name,sock, context); 244 if (name != NULL) Free(name); 245 SHUTDOWN2(sock); 246 if (i < 0) 247 { 248 SHUTDOWN2(accept_socket); 249 return(i); 250 } 251 } 252 } 253 254static int init_server_long(int *sock, int port, char *ip) 255 { 256 int ret=0; 257 struct sockaddr_in server; 258 int s= -1,i; 259 260 if (!sock_init()) return(0); 261 262 memset((char *)&server,0,sizeof(server)); 263 server.sin_family=AF_INET; 264 server.sin_port=htons((unsigned short)port); 265 if (ip == NULL) 266 server.sin_addr.s_addr=INADDR_ANY; 267 else 268/* Added for T3E, address-of fails on bit field (beckman@acl.lanl.gov) */ 269#ifndef BIT_FIELD_LIMITS 270 memcpy(&server.sin_addr.s_addr,ip,4); 271#else 272 memcpy(&server.sin_addr,ip,4); 273#endif 274 s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL); 275 276 if (s == INVALID_SOCKET) goto err; 277#if defined SOL_SOCKET && defined SO_REUSEADDR 278 { 279 int j = 1; 280 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 281 (void *) &j, sizeof j); 282 } 283#endif 284 if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1) 285 { 286#ifndef WINDOWS 287 perror("bind"); 288#endif 289 goto err; 290 } 291 /* Make it 128 for linux */ 292 if (listen(s,128) == -1) goto err; 293 i=0; 294 *sock=s; 295 ret=1; 296err: 297 if ((ret == 0) && (s != -1)) 298 { 299 SHUTDOWN(s); 300 } 301 return(ret); 302 } 303 304static int init_server(int *sock, int port) 305 { 306 return(init_server_long(sock, port, NULL)); 307 } 308 309static int do_accept(int acc_sock, int *sock, char **host) 310 { 311 int ret,i; 312 struct hostent *h1,*h2; 313 static struct sockaddr_in from; 314 int len; 315/* struct linger ling; */ 316 317 if (!sock_init()) return(0); 318 319#ifndef WINDOWS 320redoit: 321#endif 322 323 memset((char *)&from,0,sizeof(from)); 324 len=sizeof(from); 325 /* Note: under VMS with SOCKETSHR the fourth parameter is currently 326 * of type (int *) whereas under other systems it is (void *) if 327 * you don't have a cast it will choke the compiler: if you do 328 * have a cast then you can either go for (int *) or (void *). 329 */ 330 ret=accept(acc_sock,(struct sockaddr *)&from,(void *)&len); 331 if (ret == INVALID_SOCKET) 332 { 333#ifdef WINDOWS 334 i=WSAGetLastError(); 335 BIO_printf(bio_err,"accept error %d\n",i); 336#else 337 if (errno == EINTR) 338 { 339 /*check_timeout(); */ 340 goto redoit; 341 } 342 fprintf(stderr,"errno=%d ",errno); 343 perror("accept"); 344#endif 345 return(0); 346 } 347 348/* 349 ling.l_onoff=1; 350 ling.l_linger=0; 351 i=setsockopt(ret,SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling)); 352 if (i < 0) { perror("linger"); return(0); } 353 i=0; 354 i=setsockopt(ret,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i)); 355 if (i < 0) { perror("keepalive"); return(0); } 356*/ 357 358 if (host == NULL) goto end; 359#ifndef BIT_FIELD_LIMITS 360 /* I should use WSAAsyncGetHostByName() under windows */ 361 h1=gethostbyaddr((char *)&from.sin_addr.s_addr, 362 sizeof(from.sin_addr.s_addr),AF_INET); 363#else 364 h1=gethostbyaddr((char *)&from.sin_addr, 365 sizeof(struct in_addr),AF_INET); 366#endif 367 if (h1 == NULL) 368 { 369 BIO_printf(bio_err,"bad gethostbyaddr\n"); 370 *host=NULL; 371 /* return(0); */ 372 } 373 else 374 { 375 if ((*host=(char *)Malloc(strlen(h1->h_name)+1)) == NULL) 376 { 377 perror("Malloc"); 378 return(0); 379 } 380 strcpy(*host,h1->h_name); 381 382 h2=GetHostByName(*host); 383 if (h2 == NULL) 384 { 385 BIO_printf(bio_err,"gethostbyname failure\n"); 386 return(0); 387 } 388 i=0; 389 if (h2->h_addrtype != AF_INET) 390 { 391 BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n"); 392 return(0); 393 } 394 } 395end: 396 *sock=ret; 397 return(1); 398 } 399 400int extract_host_port(char *str, char **host_ptr, unsigned char *ip, 401 short *port_ptr) 402 { 403 char *h,*p; 404 405 h=str; 406 p=strchr(str,':'); 407 if (p == NULL) 408 { 409 BIO_printf(bio_err,"no port defined\n"); 410 return(0); 411 } 412 *(p++)='\0'; 413 414 if ((ip != NULL) && !host_ip(str,ip)) 415 goto err; 416 if (host_ptr != NULL) *host_ptr=h; 417 418 if (!extract_port(p,port_ptr)) 419 goto err; 420 return(1); 421err: 422 return(0); 423 } 424 425static int host_ip(char *str, unsigned char ip[4]) 426 { 427 unsigned int in[4]; 428 int i; 429 430 if (sscanf(str,"%u.%u.%u.%u",&(in[0]),&(in[1]),&(in[2]),&(in[3])) == 4) 431 { 432 for (i=0; i<4; i++) 433 if (in[i] > 255) 434 { 435 BIO_printf(bio_err,"invalid IP address\n"); 436 goto err; 437 } 438 ip[0]=in[0]; 439 ip[1]=in[1]; 440 ip[2]=in[2]; 441 ip[3]=in[3]; 442 } 443 else 444 { /* do a gethostbyname */ 445 struct hostent *he; 446 447 if (!sock_init()) return(0); 448 449 he=GetHostByName(str); 450 if (he == NULL) 451 { 452 BIO_printf(bio_err,"gethostbyname failure\n"); 453 goto err; 454 } 455 /* cast to short because of win16 winsock definition */ 456 if ((short)he->h_addrtype != AF_INET) 457 { 458 BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n"); 459 return(0); 460 } 461 ip[0]=he->h_addr_list[0][0]; 462 ip[1]=he->h_addr_list[0][1]; 463 ip[2]=he->h_addr_list[0][2]; 464 ip[3]=he->h_addr_list[0][3]; 465 } 466 return(1); 467err: 468 return(0); 469 } 470 471int extract_port(char *str, short *port_ptr) 472 { 473 int i; 474 struct servent *s; 475 476 i=atoi(str); 477 if (i != 0) 478 *port_ptr=(unsigned short)i; 479 else 480 { 481 s=getservbyname(str,"tcp"); 482 if (s == NULL) 483 { 484 BIO_printf(bio_err,"getservbyname failure for %s\n",str); 485 return(0); 486 } 487 *port_ptr=ntohs((unsigned short)s->s_port); 488 } 489 return(1); 490 } 491 492#define GHBN_NUM 4 493static struct ghbn_cache_st 494 { 495 char name[128]; 496 struct hostent ent; 497 unsigned long order; 498 } ghbn_cache[GHBN_NUM]; 499 500static unsigned long ghbn_hits=0L; 501static unsigned long ghbn_miss=0L; 502 503static struct hostent *GetHostByName(char *name) 504 { 505 struct hostent *ret; 506 int i,lowi=0; 507 unsigned long low= (unsigned long)-1; 508 509 for (i=0; i<GHBN_NUM; i++) 510 { 511 if (low > ghbn_cache[i].order) 512 { 513 low=ghbn_cache[i].order; 514 lowi=i; 515 } 516 if (ghbn_cache[i].order > 0) 517 { 518 if (strncmp(name,ghbn_cache[i].name,128) == 0) 519 break; 520 } 521 } 522 if (i == GHBN_NUM) /* no hit*/ 523 { 524 ghbn_miss++; 525 ret=gethostbyname(name); 526 if (ret == NULL) return(NULL); 527 /* else add to cache */ 528 strncpy(ghbn_cache[lowi].name,name,128); 529 memcpy((char *)&(ghbn_cache[lowi].ent),ret,sizeof(struct hostent)); 530 ghbn_cache[lowi].order=ghbn_miss+ghbn_hits; 531 return(ret); 532 } 533 else 534 { 535 ghbn_hits++; 536 ret= &(ghbn_cache[i].ent); 537 ghbn_cache[i].order=ghbn_miss+ghbn_hits; 538 return(ret); 539 } 540 } 541