📄 libhttpd.c
字号:
{ char buf[1000]; send_mime( hc, status, title, "", extrahead, "text/html", -1, 0 ); (void) sprintf( buf, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY><H2>%d %s</H2>\n", status, title, status, title ); add_response( hc, buf ); (void) sprintf( buf, form, arg ); add_response( hc, buf ); (void) sprintf( buf, "<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n", SERVER_ADDRESS, SERVER_SOFTWARE ); add_response( hc, buf ); }voidhttpd_send_err( httpd_conn* hc, int status, char* title, char* form, char* arg ) { send_response( hc, status, title, "", form, arg ); }#ifdef AUTH_FILEstatic voidsend_authenticate( httpd_conn* hc, char* realm ) { static char* header; static int maxheader = 0; static char headstr[] = "WWW-Authenticate: Basic realm=\""; realloc_str( &header, &maxheader, sizeof(headstr) + strlen( realm ) + 1 ); (void) sprintf( header, "%s%s\"", headstr, realm ); send_response( hc, 401, err401title, header, err401form, hc->encodedurl ); /* If the request was a POST then there might still be data to be read, ** so we need to do a lingering close. */ if ( hc->method == METHOD_POST ) hc->should_linger = 1; }/* Base-64 decoding. This represents binary data as printable ASCII** characters. Three 8-bit binary bytes are turned into four 6-bit** values, like so:**** [11111111] [22222222] [33333333]**** [111111] [112222] [222233] [333333]**** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".*/static int b64_decode_table[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ };/* Do base-64 decoding on a string. Ignore any non-base64 bytes.** Return the actual number of bytes generated. The decoded size will** be at most 3/4 the size of the encoded, and may be smaller if there** are padding characters (blanks, newlines).*/static intb64_decode( const char* str, unsigned char* space, int size ) { const char* cp; int space_idx, phase; int d, prev_d; unsigned char c; space_idx = 0; phase = 0; for ( cp = str; *cp != '\0'; ++cp ) { d = b64_decode_table[*cp]; if ( d != -1 ) { switch ( phase ) { case 0: ++phase; break; case 1: c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 2: c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 3: c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); if ( space_idx < size ) space[space_idx++] = c; phase = 0; break; } prev_d = d; } } return space_idx; }static intauth_check( httpd_conn* hc, char* dirname ) { static char* authpath; static int maxauthpath = 0; struct stat sb; char authinfo[500]; char* authpass; int l; FILE* fp; char line[500]; char* cryp; static char* prevauthpath; static int maxprevauthpath = 0; static time_t prevmtime; static char* prevuser; static int maxprevuser = 0; static char* prevcryp; static int maxprevcryp = 0; /* Construct auth filename. */ realloc_str( &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) ); (void) sprintf( authpath, "%s/%s", dirname, AUTH_FILE ); /* Does this directory have an auth file? */ if ( stat( authpath, &sb ) < 0 ) /* Nope, let the request go through. */ return 0; /* Does this request contain authorization info? */ if ( hc->authorization[0] == '\0' ) { /* Nope, return a 401 Unauthorized. */ send_authenticate( hc, dirname ); return -1; } /* Basic authorization info? */ if ( strncmp( hc->authorization, "Basic ", 6 ) != 0 ) { send_authenticate( hc, dirname ); return -1; } /* Decode it. */ l = b64_decode( &(hc->authorization[6]), authinfo, sizeof(authinfo) ); authinfo[l] = '\0'; /* Split into user and password. */ authpass = strchr( authinfo, ':' ); if ( authpass == (char*) 0 ) { /* No colon? Bogus auth info. */ send_authenticate( hc, dirname ); return -1; } *authpass++ = '\0'; /* See if we have a cached entry and can use it. */ if ( maxprevauthpath != 0 && strcmp( authpath, prevauthpath ) == 0 && sb.st_mtime == prevmtime && strcmp( authinfo, prevuser ) == 0 ) { /* Yes. Check against the cached encrypted password. */ if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 ) { /* Ok! */ realloc_str( &hc->remoteuser, &hc->maxremoteuser, strlen( line ) ); (void) strcpy( hc->remoteuser, line ); return 0; } else { /* No. */ send_authenticate( hc, dirname ); return -1; } } /* Open the password file. */ fp = fopen( authpath, "r" ); if ( fp == (FILE*) 0 ) { /* The file exists but we can't open it? Disallow access. */ syslog( LOG_ERR, "fopen auth file %.80s - %m", authpath ); httpd_send_err( hc, 403, err403title, err403form, hc->encodedurl ); return -1; } /* Read it. */ while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) { /* Nuke newline. */ l = strlen( line ); if ( line[l - 1] == '\n' ) line[l - 1] = '\0'; /* Split into user and encrypted password. */ cryp = strchr( line, ':' ); if ( cryp == (char*) 0 ) continue; *cryp++ = '\0'; /* Is this the right user? */ if ( strcmp( line, authinfo ) == 0 ) { /* Yes. */ (void) fclose( fp ); /* So is the password right? */ if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 ) { /* Ok! */ realloc_str( &hc->remoteuser, &hc->maxremoteuser, strlen( line ) ); (void) strcpy( hc->remoteuser, line ); /* And cache this user's info for next time. */ realloc_str( &prevauthpath, &maxprevauthpath, strlen( authpath ) ); (void) strcpy( prevauthpath, authpath ); prevmtime = sb.st_mtime; realloc_str( &prevuser, &maxprevuser, strlen( authinfo ) ); (void) strcpy( prevuser, authinfo ); realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) ); (void) strcpy( prevcryp, cryp ); return 0; } else { /* No. */ send_authenticate( hc, dirname ); return -1; } } } /* Didn't find that user. Access denied. */ (void) fclose( fp ); send_authenticate( hc, dirname ); return -1; }#endif /* AUTH_FILE */static voidsend_dirredirect( httpd_conn* hc ) { static char* location; static char* header; static int maxlocation = 0, maxheader = 0; static char headstr[] = "Location: "; realloc_str( &location, &maxlocation, strlen( hc->encodedurl ) + 1 ); (void) sprintf( location, "%s/", hc->encodedurl ); realloc_str( &header, &maxheader, sizeof(headstr) + strlen( location ) ); (void) sprintf( header, "%s%s", headstr, location ); send_response( hc, 302, err302title, header, err302form, location ); }char*httpd_method_str( int method ) { switch ( method ) { case METHOD_GET: return "GET"; case METHOD_HEAD: return "HEAD"; case METHOD_POST: return "POST"; default: return (char*) 0; } }static intis_hexit( char c ) { if ( strchr( "0123456789abcdefABCDEF", c ) != (char*) 0 ) return 1; return 0; }static inthexit( char c ) { if ( c >= '0' && c <= '9' ) return c - '0'; if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10; if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10; return 0; /* shouldn't happen, we're guarded by is_hexit() */ }/* Copies and decodes a string. It's ok for from and to to be the** same string.*/static voidstrdecode( char* to, char* from ) { for ( ; *from != '\0'; ++to, ++from ) { if ( from[0] == '%' && is_hexit( from[1] ) && is_hexit( from[2] ) ) { *to = hexit( from[1] ) * 16 + hexit( from[2] ); from += 2; } else *to = *from; } *to = '\0'; }inthttpd_get_nfiles( void ) {#ifdef EMBED return(8);#else static int inited = 0; static int n; if ( ! inited ) {#ifdef RLIMIT_NOFILE struct rlimit rl; if ( getrlimit( RLIMIT_NOFILE, &rl ) < 0 ) { syslog( LOG_ERR, "getrlimit - %m" ); exit( 1 ); } if ( rl.rlim_max == RLIM_INFINITY ) rl.rlim_cur = 4096; /* arbitrary */ else rl.rlim_cur = rl.rlim_max; if ( setrlimit( RLIMIT_NOFILE, &rl ) < 0 ) { syslog( LOG_ERR, "setrlimit - %m" ); exit( 1 ); } n = rl.rlim_cur;#else /* RLIMIT_NOFILE */ n = getdtablesize();#endif /* RLIMIT_NOFILE */ inited = 1; } return n;#endif }/* Map a ~username/whatever URL into something else. Two different ways. */static inttilde_map( httpd_conn* hc ) {#if defined(TILDE_MAP_1) || defined(TILDE_MAP_2) static char* temp; static int maxtemp = 0;#endif#ifdef TILDE_MAP_1 /* Map ~username to <prefix>/username. */ int len; static char* prefix = TILDE_MAP_1; len = strlen( hc->expnfilename ) - 1; realloc_str( &temp, &maxtemp, len ); (void) strcpy( temp, &hc->expnfilename[1] ); realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len ); (void) strcpy( hc->expnfilename, prefix ); if ( prefix[0] != '\0' ) (void) strcat( hc->expnfilename, "/" ); (void) strcat( hc->expnfilename, temp );#endif /* TILDE_MAP_1 */#ifdef TILDE_MAP_2 /* Map ~username to <user's homedir>/<postfix>. */ static char* postfix = TILDE_MAP_2; char* cp; struct passwd* pw; /* Get the username. */ realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 ); (void) strcpy( temp, &hc->expnfilename[1] ); cp = strchr( temp, '/' ); if ( cp != (char*) 0 ) *cp++ = '\0'; else cp = ""; /* Get the passwd entry. */ pw = getpwnam( temp ); if ( pw == (struct passwd*) 0 ) return 0; /* Set up altdir. */ realloc_str( &hc->altdir, &hc->maxaltdir, strlen( pw->pw_dir ) + 1 + strlen( postfix ) ); (void) strcpy( hc->altdir, pw->pw_dir ); if ( postfix[0] != '\0' ) { (void) strcat( hc->altdir, "/" ); (void) strcat( hc->altdir, postfix ); } /* And the filename becomes altdir plus the post-~ part of the original. */ realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->altdir ) + 1 + strlen( cp ) ); (void) sprintf( hc->expnfilename, "%s/%s", hc->altdir, cp );#endif /* TILDE_MAP_2 */ return 1; }/* Expands all symlinks in the given filename, eliding ..'s and leading /'s.** Returns the expanded path (pointer to static string), or (char*) 0 on** errors. Also returns, in the string pointed to by restP, any trailing** parts of the path that don't exist.**** This is a fairly nice little routine. It handles any size filenames** without excessive mallocs.*/static char*expand_symlinks( char* path, char** restP, int chrooted ) { static char* checked; static char* rest;#ifdef EMBED char link[2048];#else char link[5000];#endif static int maxchecked = 0, maxrest = 0; int checkedlen, restlen, linklen, prevcheckedlen, prevrestlen, nlinks, i; char* r; char* cp1; char* cp2; if ( chrooted ) { /* If we are chrooted, we can actually skip the symlink-expansion, ** since it's impossible to get out of the tree. However, we still ** need to do the pathinfo check, and the existing symlink expansion ** code is a pretty reasonable way to do this. So, what we do is ** a single stat() of the whole filename - if it exists, then we ** return it as is with nothing in restP. If it doesn't exist, we ** fall through to the existing code. ** ** One side-effect of this is that users can't symlink to central ** approved CGIs any more. The workaround is to use the central ** URL for the CGI instead of a local symlinked one. */ struct stat sb; if ( stat( path, &sb ) != -1 ) { realloc_str( &checked, &maxchecked, strlen( path ) ); (void) strcpy( checked, path ); realloc_str( &rest, &maxrest, 0 ); rest[0] = '\0'; *restP = rest; return checked; } } /* Start out with nothing in checked and the whole filename in rest. */ realloc_str( &checked, &maxchecked, 1 ); checked[0] = '\0'; checkedlen = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -