#include "handle.h" #define HTML_TPL "%s

%s

" #ifdef USE_OPENSSL int usessl() { return 0; } #endif void error_log(const char* fmt, ...) { UNUSED(fmt); return; } #ifdef DEBUG void server_log(const char* fmt, ...) { UNUSED(fmt); return; } #endif const char* get_status_str(int stat) { switch(stat) { case 100: return "Continue"; case 101: return "Switching Protocols"; case 102: return "Processing"; case 103: return "Early Hints"; case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; case 203: return "Non-Authoritative Information"; case 204: return "No Content"; case 205: return "Reset Content"; case 206: return "Partial Content"; case 207: return "Multi-Status"; case 208: return "Already Reported"; case 226: return "IM Used"; case 300: return "Multiple Choices"; case 301: return "Moved Permanently"; case 302: return "Found"; case 303: return "See Other"; case 304: return "Not Modified"; case 305: return "Use Proxy"; case 306: return "Switch Proxy"; case 307: return "Temporary Redirect"; case 308: return "Permanent Redirect"; case 400: return "Bad Request"; case 401: return "Unauthorized"; case 402: return "Payment Required"; case 403: return "Forbidden"; case 404: return "Not Found"; case 405: return "Method Not Allowed"; case 406: return "Not Acceptable"; case 407: return "Proxy Authentication Required"; case 408: return "Request Timeout"; case 409: return "Conflict"; case 410: return "Gone"; case 411: return "Length Required"; case 412: return "Precondition Failed"; case 413: return "Payload Too Large"; case 414: return "URI Too Long"; case 415: return "Unsupported Media Type"; case 416: return "Range Not Satisfiable"; case 417: return "Expectation Failed"; case 421: return "Misdirected Request"; case 422: return "Unprocessable Entity"; case 423: return "Locked"; case 424: return "Failed Dependency"; case 425: return "Too Early"; case 426: return "Upgrade Required"; case 428: return "Precondition Required"; case 429: return "Too Many Requests"; case 431: return "Request Header Fields Too Large"; case 451: return "Unavailable For Legal Reasons"; case 500: return "Internal Server Error"; case 501: return "Not Implemented"; case 502: return "Bad Gateway"; case 503: return "Service Unavailable"; case 504: return "Gateway Timeout"; case 505: return "HTTP Version Not Supported"; case 506: return "Variant Also Negotiates"; case 507: return "Insufficient Storage"; case 508: return "Loop Detected"; case 510: return "Not Extended"; case 511: return "Network Authentication Required"; default: return "Unofficial Status"; } } void antd_send_header(void* client, antd_response_header_t* res) { if(!res->header) res->header = dict(); dput(res->header,"Server", strdup(SERVER_NAME)); const char* stat_str = get_status_str(res->status); __t(client, "HTTP/1.1 %d %s", res->status, stat_str); chain_t it; for_each_assoc(it, res->header) { __t(client,"%s: %s", it->key, (const char*)it->value); } // send out cookie if(res->cookie) { int size = list_size(res->cookie); for (int i = 0; i < size; i++) { __t(client,"Set-Cookie: %s", list_at(res->cookie, i)->value.s); } list_free(&res->cookie); res->cookie = NULL; } __b(client, (unsigned char*)"\r\n", 2); freedict(res->header); res->header = NULL; } /* void octstream(void* client, char* name) { set_status(client,200,"OK"); __t(client,"Content-Type: application/octet-stream"); __t(client,"Content-Disposition: attachment; filename=\"%s\"", name); response(client,""); //Content-Disposition: attachment; filename="fname.ext" }*/ int antd_send(void *src, const void* data, int len) { if(!src || !data) return -1; int written; antd_client_t * source = (antd_client_t *) src; char* ptr; int writelen = 0; int count; #ifdef USE_OPENSSL if(usessl()) { //LOG("SSL WRITE\n"); //ret = SSL_write((SSL*) source->ssl, data, len); ptr = (char* )data; writelen = len > BUFFLEN?BUFFLEN:len; written = 0; fd_set fds; struct timeval timeout; while (writelen > 0) //source->attempt < MAX_ATTEMPT { // clear the error queue ERR_clear_error(); count = SSL_write (source->ssl, ptr+written, writelen); if (count > 0) { written += count; writelen = (len - written) > BUFFLEN?BUFFLEN:(len-written); } else { //printf(" received equal to or less than 0\n") int err = SSL_get_error(source->ssl, count); switch (err) { case SSL_ERROR_NONE: { // no real error, just try again... //LOG("SSL_ERROR_NONE \n"); //source->attempt++; continue; } case SSL_ERROR_ZERO_RETURN: { // peer disconnected... ERROR("SSLWRITE: SSL_ERROR_ZERO_RETURN: peer disconected: %d", source->sock); break; } case SSL_ERROR_WANT_READ: { // no data available right now, wait a few seconds in case new data arrives... //printf("SSL_ERROR_WANT_READ\n"); int sock = SSL_get_rfd(source->ssl); FD_ZERO(&fds); FD_SET(sock, &fds); timeout.tv_sec = 0; timeout.tv_usec = 500; err = select(sock+1, &fds, NULL, NULL, &timeout); if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) { //source->attempt++; continue; // more data to read... } ERROR("SSL WRITE: want read but select error on the socket %d: %s", source->sock, strerror(errno)); break; } case SSL_ERROR_WANT_WRITE: { // socket not writable right now, wait a few seconds and try again... //printf("SSL_ERROR_WANT_WRITE \n"); int sock = SSL_get_wfd(source->ssl); FD_ZERO(&fds); FD_SET(sock, &fds); timeout.tv_sec = 0; timeout.tv_usec = 500; err = select(sock+1, NULL, &fds, NULL, &timeout); if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) { //source->attempt++; continue; // can write more data now... } ERROR("SSL WRITE: want write but select error on the socket %d: %s", source->sock, strerror(errno)); break; } default: { // other error ERROR("SSL WRITE: Unknown error on %d: %s", source->sock, ERR_error_string(ERR_get_error(), NULL) ); break; } } break; } } //source->attempt = 0; } else { #endif ptr = (char* )data; writelen = len > BUFFLEN?BUFFLEN:len; written = 0; while (writelen > 0) { count = send(source->sock, ptr+written, writelen, 0); if (count > 0) { written += count; writelen = (len - written) > BUFFLEN?BUFFLEN:(len-written); } else if(count == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { ERROR("Error while writing: %s", strerror(errno)); break; //return written; } } #ifdef USE_OPENSSL } #endif /*if(ret <= 0) { antd_close(src); }*/ if(written > 0) time(&source->last_io); return written; } int antd_recv(void *src, void* data, int len) { if(!src) return -1; int read=0; char* ptr = NULL; int received=0; int readlen=0; antd_client_t * source = (antd_client_t *) src; #ifdef USE_OPENSSL if(usessl()) { ptr = (char* )data; readlen = len > BUFFLEN?BUFFLEN:len; read = 0; fd_set fds; struct timeval timeout; while (readlen > 0 )//&& source->attempt < MAX_ATTEMPT { ERR_clear_error(); received = SSL_read (source->ssl, ptr+read, readlen); if (received > 0) { read += received; readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read); } else { //printf(" received equal to or less than 0\n") int err = SSL_get_error(source->ssl, received); switch (err) { case SSL_ERROR_NONE: { // no real error, just try again... //LOG("SSL_ERROR_NONE \n"); //source->attempt++; continue; } case SSL_ERROR_ZERO_RETURN: { // peer disconnected... ERROR("SSL READ: SSL_ERROR_ZERO_RETURN, peer disconnected %d", source->sock); break; } case SSL_ERROR_WANT_READ: { // no data available right now, wait a few seconds in case new data arrives... //printf("SSL_ERROR_WANT_READ\n"); int sock = SSL_get_rfd(source->ssl); FD_ZERO(&fds); FD_SET(sock, &fds); timeout.tv_sec = 0; timeout.tv_usec = 500; err = select(sock+1, &fds, NULL, NULL, &timeout); if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) { //source->attempt++; continue; // more data to read... } ERROR("SSL READ: want read but select error on the socket %d: %s", source->sock, strerror(errno)); break; } case SSL_ERROR_WANT_WRITE: { // socket not writable right now, wait a few seconds and try again... //printf("SSL_ERROR_WANT_WRITE \n"); int sock = SSL_get_wfd(source->ssl); FD_ZERO(&fds); FD_SET(sock, &fds); timeout.tv_sec = 0; timeout.tv_usec = 500; err = select(sock+1, NULL, &fds, NULL, &timeout); if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) { //source->attempt++; continue; // can write more data now... } ERROR("SSL READ: want write but select error on the socket %d: %s", source->sock, strerror(errno)); break; } default: { // other error ERROR("SSL READ: unkown error on %d: %s", source->sock, ERR_error_string(ERR_get_error(), NULL)); break; } } break; } } //source->attempt = 0; /* int stat, r, st; do{ ret = SSL_read((SSL*) source->ssl, data, len); stat = SSL_get_error((SSL*)source->ssl, r); } while(ret == -1 && ( stat == SSL_ERROR_WANT_READ || stat == SSL_ERROR_WANT_WRITE || stat == SSL_ERROR_NONE || (stat == SSL_ERROR_SYSCALL && r== 0 && !ERR_get_error()) )); if(ret == -1) { LOG("Problem reading %d %d %d\n", ret, stat, r); } //set_nonblock(source->sock); */ } else { #endif ptr = (char* )data; readlen = len > BUFFLEN?BUFFLEN:len; read = 0; while (readlen > 0 )//&& source->attempt < MAX_ATTEMPT { received = recv(((int) source->sock), ptr+read, readlen, 0); //LOG("Read : %c\n", *ptr); if (received > 0) { read += received; readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read); //LOG("Read len is %d\n", readlen); } else if(errno != EAGAIN && errno != EWOULDBLOCK) { ERROR("Error while writing: %s", strerror(errno)); break; } } //read = recv(((int) source->sock), data, len, 0); #ifdef USE_OPENSSL } #endif //LOG("Received %d bytes\n", read); /*if(ret == 0) { antd_close(src); }*/ if(read > 0) time(&source->last_io); return read; } void set_nonblock(int socket) { int flags; flags = fcntl(socket,F_GETFL,0); //assert(flags != -1); fcntl(socket, F_SETFL, flags | O_NONBLOCK); } /*void set_block() { int flags; flags = fcntl(socket,F_GETFL,0); //assert(flags != -1); fcntl(socket, F_SETFL, flags & (~O_NONBLOCK)); }*/ int antd_close(void* src) { if(!src) return -1; antd_client_t * source = (antd_client_t *) src; #ifdef USE_OPENSSL if(source->ssl && usessl()){ //printf("SSL:Shutdown ssl\n"); //SSL_shutdown((SSL*) source->ssl); SSL_set_shutdown((SSL*) source->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); //printf("SSL:Free ssl\n"); SSL_free((SSL*) source->ssl); //EVP_cleanup(); //ENGINE_cleanup(); CRYPTO_cleanup_all_ex_data(); ERR_remove_state(0); ERR_free_strings(); source->ssl = NULL; //LOG("Freeing SSL\n"); } #endif //printf("Close sock %d\n", source->sock); int ret = close(source->sock); if(source->ip) free(source->ip); free(src); src = NULL; return ret; } int __t(void* client, const char* fstring,...) { int dlen; int st; va_list arguments; char * data; va_start( arguments, fstring); dlen = vsnprintf(0,0,fstring,arguments)+1; va_end(arguments); if ((data = (char*)malloc(dlen*sizeof(char))) != 0) { va_start(arguments, fstring); vsnprintf(data, dlen, fstring, arguments); va_end(arguments); st = __b(client, (const unsigned char*)data, strlen(data)); __b(client, (unsigned char*)"\r\n", 2); free(data); return st; } return 0; // } int __b(void* client, const unsigned char* data, int size) { char buf[BUFFLEN]; int sent = 0; int buflen = 0; int nbytes = 0; /*if(size <= BUFFLEN) { nbytes = antd_send(client,data,size); return (nbytes==-1?0:1); } else {*/ while(sent < size) { if(size - sent > BUFFLEN) buflen = BUFFLEN; else buflen = size - sent; memcpy(buf,data+sent,buflen); nbytes = antd_send(client,buf,buflen); if(nbytes == -1) { return 0; } sent += nbytes; } //} return 1; } int __f(void* client, const char* file) { unsigned char buffer[BUFFLEN]; FILE *ptr; ptr = fopen(file,"rb"); if(!ptr) { LOG("Cannot read : %s", file); return 0; } size_t size; while(!feof(ptr)) { size = fread(buffer,1,BUFFLEN,ptr); if(!__b(client,buffer,size)) return 0; } fclose(ptr); return 1; } int upload(const char* tmp, const char* path) { return !rename(tmp, path); } /* void set_cookie(void* client,const char* type, dictionary_t dic, const char* name) { set_status(client,200,"OK"); __t(client,"Content-Type: %s",type); chain_t assoc; for_each_assoc(assoc,dic){ __t(client,"Set-Cookie: %s=%s; Path=/%s",assoc->key, (char*)assoc->value, name); } response(client,""); } void clear_cookie(void* client, dictionary_t dic) { set_status(client,200,"OK"); __t(client,"Content-Type: text/html; charset=utf-8"); chain_t assoc; for_each_assoc(assoc,dic){ __t(client,"Set-Cookie: %s=%s;expires=%s",assoc->key, (char*)assoc->value, server_time()); } response(client,""); } */ void antd_error(void* client, int status, const char* msg) { antd_response_header_t rsh; rsh.header = dict(); rsh.cookie = NULL; const char* stat_str = get_status_str(status); rsh.status = status; dput(rsh.header, "Content-Type", strdup("text/html; charset=utf-8")); char * res_str = __s(HTML_TPL, stat_str, msg); int clen = 0; if(res_str) { clen = strlen(res_str); } char ibuf[20]; snprintf (ibuf, sizeof(ibuf), "%d",clen); dput(rsh.header, "Content-Length", strdup(ibuf)); antd_send_header(client, &rsh); if(res_str) { //printf("%s\n", res_str); __b(client, (unsigned char*)res_str, clen); //__t(client, HTML_TPL, stat_str, msg); free(res_str); } } int ws_enable(dictionary_t dic) { if(!dic) return 0; char*v = (char*)dvalue(dic, "__web_socket__"); if(!v) return 0; return atoi(v) == 1; } /** * read the request as a string line format * @param sock socket * @return a request string */ char* read_line(void* sock) { char buf[BUFFLEN]; read_buf(sock,buf,sizeof(buf)); return strdup(buf); } /** * Read the socket request in to a buffer or size * The data is read until the buffer is full or * there are a carrier return character * @param sock socket * @param buf buffer * @param size size of buffer * @return number of bytes read */ int read_buf(void* sock, char*buf,int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = antd_recv(sock, &c, 1); if (n > 0) { //LOG("Data : %c\n", c); buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return i; } /* We put it here since we want the plugin is able to destroy the request if it want to in this case, the plugin should return an empty with no data */ void destroy_request(void *data) { if (!data) return; antd_request_t *rq = (antd_request_t *)data; LOG("Close request %d", rq->client->sock); // free all other thing if (rq->request) { dictionary_t tmp = dvalue(rq->request, "COOKIE"); if (tmp) freedict(tmp); tmp = dvalue(rq->request, "REQUEST_HEADER"); if (tmp) freedict(tmp); tmp = dvalue(rq->request, "REQUEST_DATA"); if (tmp) freedict(tmp); dput(rq->request, "REQUEST_HEADER", NULL); dput(rq->request, "REQUEST_DATA", NULL); dput(rq->request, "COOKIE", NULL); freedict(rq->request); } antd_close(rq->client); free(rq); }