/* * syscall_random(): Try to get random data using a system call * returns the number of bytes returned in buf, or < 0 on error. */ static ssize_t syscall_random(void *buf, size_t buflen) { /* * Note: 'buflen' equals the size of the buffer which is used by the * get_entropy() callback of the RAND_DRBG. It is roughly bounded by * * 2 * RAND_POOL_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^14 * * which is way below the OSSL_SSIZE_MAX limit. Therefore sign conversion * between size_t and ssize_t is safe even without a range check. */ /* * Do runtime detection to find getentropy(). * * Known OSs that should support this: * - Darwin since 16 (OSX 10.12, IOS 10.0). * - Solaris since 11.3 * - OpenBSD since 5.6 * - Linux since 3.17 with glibc 2.25 * - FreeBSD since 12.0 (1200061) */ # if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) extern int getentropy(void *buffer, size_t length) __attribute__((weak)); if (getentropy != NULL) return getentropy(buf, buflen) == 0 ? (ssize_t)buflen : -1; # else union { void *p; int (*f)(void *buffer, size_t length); } p_getentropy; /* * We could cache the result of the lookup, but we normally don't * call this function often. */ ERR_set_mark(); p_getentropy.p = DSO_global_lookup("getentropy"); ERR_pop_to_mark(); if (p_getentropy.p != NULL) return p_getentropy.f(buf, buflen) == 0 ? (ssize_t)buflen : -1; # endif /* Linux supports this since version 3.17 */ # if defined(__linux) && defined(SYS_getrandom) return syscall(SYS_getrandom, buf, buflen, 0); # elif (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) return sysctl_random(buf, buflen); # else errno = ENOSYS; return -1; # endif }
/* * syscall_random(): Try to get random data using a system call * returns the number of bytes returned in buf, or <= 0 on error. */ int syscall_random(void *buf, size_t buflen) { /* * Do runtime detection to find getentropy(). * * Known OSs that should support this: * - Darwin since 16 (OSX 10.12, IOS 10.0). * - Solaris since 11.3 * - OpenBSD since 5.6 * - Linux since 3.17 with glibc 2.25 * - FreeBSD since 12.0 (1200061) */ # if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) extern int getentropy(void *bufer, size_t length) __attribute__((weak)); if (getentropy != NULL) return getentropy(buf, buflen) == 0 ? buflen : 0; # else union { void *p; int (*f)(void *buffer, size_t length); } p_getentropy; /* * We could cache the result of the lookup, but we normally don't * call this function often. */ ERR_set_mark(); p_getentropy.p = DSO_global_lookup("getentropy"); ERR_pop_to_mark(); if (p_getentropy.p != NULL) return p_getentropy.f(buf, buflen) == 0 ? buflen : 0; # endif /* Linux supports this since version 3.17 */ # if defined(__linux) && defined(SYS_getrandom) return (int)syscall(SYS_getrandom, buf, buflen, 0); # endif # if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) return (int)sysctl_random(buf, buflen); # endif return -1; }
int BIO_accept(int sock, char **addr) { int ret=INVALID_SOCKET; unsigned long l; unsigned short port; char *p; struct { /* * As for following union. Trouble is that there are platforms * that have socklen_t and there are platforms that don't, on * some platforms socklen_t is int and on some size_t. So what * one can do? One can cook #ifdef spaghetti, which is nothing * but masochistic. Or one can do union between int and size_t. * One naturally does it primarily for 64-bit platforms where * sizeof(int) != sizeof(size_t). But would it work? Note that * if size_t member is initialized to 0, then later int member * assignment naturally does the job on little-endian platforms * regardless accept's expectations! What about big-endians? * If accept expects int*, then it works, and if size_t*, then * length value would appear as unreasonably large. But this * won't prevent it from filling in the address structure. The * trouble of course would be if accept returns more data than * actual buffer can accomodate and overwrite stack... That's * where early OPENSSL_assert comes into picture. Besides, the * only 64-bit big-endian platform found so far that expects * size_t* is HP-UX, where stack grows towards higher address. * <appro> */ union { size_t s; int i; } len; union { struct sockaddr sa; struct sockaddr_in sa_in; #if OPENSSL_USE_IPV6 struct sockaddr_in6 sa_in6; #endif } from; } sa; sa.len.s=0; sa.len.i=sizeof(sa.from); memset(&sa.from,0,sizeof(sa.from)); ret=accept(sock,&sa.from.sa,(void *)&sa.len); if (sizeof(sa.len.i)!=sizeof(sa.len.s) && sa.len.i==0) { OPENSSL_assert(sa.len.s<=sizeof(sa.from)); sa.len.i = (unsigned int)sa.len.s; } if (ret == INVALID_SOCKET) { if(BIO_sock_should_retry(ret)) return -2; SYSerr(SYS_F_ACCEPT,get_last_socket_error()); BIOerr(BIO_F_BIO_ACCEPT,BIO_R_ACCEPT_ERROR); goto end; } if (addr == NULL) goto end; #ifdef EAI_FAMILY do { char h[NI_MAXHOST],s[NI_MAXSERV]; size_t nl; static union { void *p; int (*f)(const struct sockaddr *,size_t/*socklen_t*/, char *,size_t,char *,size_t,int); } p_getnameinfo = {NULL}; /* 2nd argument to getnameinfo is specified to * be socklen_t. Unfortunately there is a number * of environments where socklen_t is not defined. * As it's passed by value, it's safe to pass it * as size_t... <appro> */ if (p_getnameinfo.p==NULL) { if ((p_getnameinfo.p=DSO_global_lookup("getnameinfo"))==NULL) p_getnameinfo.p=(void*)-1; } if (p_getnameinfo.p==(void *)-1) break; if ((*p_getnameinfo.f)(&sa.from.sa,sa.len.i,h,sizeof(h),s,sizeof(s), NI_NUMERICHOST|NI_NUMERICSERV)) break; nl = strlen(h)+strlen(s)+2; p = *addr; if (p) { *p = '\0'; p = OPENSSL_realloc(p,nl); } else { p = OPENSSL_malloc(nl); } if (p==NULL) { BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE); goto end; } *addr = p; BIO_snprintf(*addr,nl,"%s:%s",h,s); goto end; } while(0); #endif if (sa.from.sa.sa_family != AF_INET) goto end; l=ntohl(sa.from.sa_in.sin_addr.s_addr); port=ntohs(sa.from.sa_in.sin_port); if (*addr == NULL) { if ((p=OPENSSL_malloc(24)) == NULL) { BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE); goto end; } *addr=p; } BIO_snprintf(*addr,24,"%d.%d.%d.%d:%d", (unsigned char)(l>>24L)&0xff, (unsigned char)(l>>16L)&0xff, (unsigned char)(l>> 8L)&0xff, (unsigned char)(l )&0xff, port); end: return(ret); }
int BIO_get_accept_socket(char *host, int bind_mode) { int ret=0; union { struct sockaddr sa; struct sockaddr_in sa_in; #if OPENSSL_USE_IPV6 struct sockaddr_in6 sa_in6; #endif } server,client; int s=INVALID_SOCKET,cs; unsigned char ip[4]; unsigned short port; char *str=NULL,*e; char *h,*p; unsigned long l; int err_num; if (BIO_sock_init() != 1) return(INVALID_SOCKET); if ((str=BUF_strdup(host)) == NULL) return(INVALID_SOCKET); h=p=NULL; h=str; for (e=str; *e; e++) { if (*e == ':') { p=e; } else if (*e == '/') { *e='\0'; break; } } if (p) *p++='\0'; /* points at last ':', '::port' is special [see below] */ else p=h,h=NULL; #ifdef EAI_FAMILY do { static union { void *p; int (*f)(const char *,const char *, const struct addrinfo *, struct addrinfo **); } p_getaddrinfo = {NULL}; static union { void *p; void (*f)(struct addrinfo *); } p_freeaddrinfo = {NULL}; struct addrinfo *res,hint; if (p_getaddrinfo.p==NULL) { if ((p_getaddrinfo.p=DSO_global_lookup("getaddrinfo"))==NULL || (p_freeaddrinfo.p=DSO_global_lookup("freeaddrinfo"))==NULL) p_getaddrinfo.p=(void*)-1; } if (p_getaddrinfo.p==(void *)-1) break; /* '::port' enforces IPv6 wildcard listener. Some OSes, * e.g. Solaris, default to IPv6 without any hint. Also * note that commonly IPv6 wildchard socket can service * IPv4 connections just as well... */ memset(&hint,0,sizeof(hint)); if (h) { if (strchr(h,':')) { if (h[1]=='\0') h=NULL; #if OPENSSL_USE_IPV6 hint.ai_family = AF_INET6; #else h=NULL; #endif } else if (h[0]=='*' && h[1]=='\0') h=NULL; } if ((*p_getaddrinfo.f)(h,p,&hint,&res)) break; memcpy(&server, res->ai_addr, res->ai_addrlen<=sizeof(server)?res->ai_addrlen:sizeof(server)); (*p_freeaddrinfo.f)(res); goto again; } while (0); #endif if (!BIO_get_port(p,&port)) goto err; memset((char *)&server,0,sizeof(server)); server.sa_in.sin_family=AF_INET; server.sa_in.sin_port=htons(port); if (h == NULL || strcmp(h,"*") == 0) server.sa_in.sin_addr.s_addr=INADDR_ANY; else { if (!BIO_get_host_ip(h,&(ip[0]))) goto err; l=(unsigned long) ((unsigned long)ip[0]<<24L)| ((unsigned long)ip[1]<<16L)| ((unsigned long)ip[2]<< 8L)| ((unsigned long)ip[3]); server.sa_in.sin_addr.s_addr=htonl(l); } again: s=socket(server.sa.sa_family,SOCK_STREAM,SOCKET_PROTOCOL); if (s == INVALID_SOCKET) { SYSerr(SYS_F_SOCKET,get_last_socket_error()); ERR_add_error_data(3,"port='",host,"'"); BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_CREATE_SOCKET); goto err; } #ifdef SO_REUSEADDR if (bind_mode == BIO_BIND_REUSEADDR) { int i=1; ret=setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i)); bind_mode=BIO_BIND_NORMAL; } #endif if (bind(s,&server.sa,sizeof(server)) == -1) { #ifdef SO_REUSEADDR err_num=get_last_socket_error(); if ((bind_mode == BIO_BIND_REUSEADDR_IF_UNUSED) && (err_num == EADDRINUSE)) { client = server; if (h == NULL || strcmp(h,"*") == 0) { #if OPENSSL_USE_IPV6 if (client.sa.sa_family == AF_INET6) { memset(&client.sa_in6.sin6_addr,0,sizeof(client.sa_in6.sin6_addr)); client.sa_in6.sin6_addr.s6_addr[15]=1; } else #endif if (client.sa.sa_family == AF_INET) { client.sa_in.sin_addr.s_addr=htonl(0x7F000001); } else goto err; } cs=socket(client.sa.sa_family,SOCK_STREAM,SOCKET_PROTOCOL); if (cs != INVALID_SOCKET) { int ii; ii=connect(cs,&client.sa,sizeof(client)); closesocket(cs); if (ii == INVALID_SOCKET) { bind_mode=BIO_BIND_REUSEADDR; closesocket(s); goto again; } /* else error */ } /* else error */ } #endif SYSerr(SYS_F_BIND,err_num); ERR_add_error_data(3,"port='",host,"'"); BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_BIND_SOCKET); goto err; } if (listen(s,MAX_LISTEN) == -1) { SYSerr(SYS_F_BIND,get_last_socket_error()); ERR_add_error_data(3,"port='",host,"'"); BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_LISTEN_SOCKET); goto err; } ret=1; err: if (str != NULL) OPENSSL_free(str); if ((ret == 0) && (s != INVALID_SOCKET)) { closesocket(s); s= INVALID_SOCKET; } return(s); }