/* Select handler which is set for the socket descriptor when connect() has * indicated (via errno) that it is in progress. On completion this handler gets * called. */ static void connected(struct socket *socket) { int err = 0; struct connection_state state = connection_state(0); socklen_t len = sizeof(err); assertm(socket->connect_info != NULL, "Lost connect_info!"); if_assert_failed return; if (getsockopt(socket->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == 0) { /* Why does EMX return so large values? */ if (err >= 10000) err -= 10000; if (err != 0) state = connection_state_for_errno(err); else state = connection_state(0); } else { /* getsockopt() failed */ if (errno != 0) state = connection_state_for_errno(errno); else state = connection_state(S_STATE); } if (!is_in_state(state, 0)) { /* There are maybe still some more candidates. */ connect_socket(socket, state); return; } complete_connect_socket(socket, NULL, NULL); }
static void ssl_want_read(struct socket *socket) { if (socket->no_tls) ssl_set_no_tls(socket); switch (ssl_do_connect(socket)) { case SSL_ERROR_NONE: #ifdef CONFIG_GNUTLS if (get_opt_bool((const unsigned char *)"connection.ssl.cert_verify", NULL) && verify_certificates(socket)) { socket->ops->retry(socket, connection_state(S_SSL_ERROR)); return; } #endif /* Report successful SSL connection setup. */ complete_connect_socket(socket, NULL, NULL); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ2: break; default: socket->no_tls = !socket->no_tls; socket->ops->retry(socket, connection_state(S_SSL_ERROR)); } }
static void fsp_directory(FSP_SESSION *ses, struct uri *uri) { struct string buf; FSP_DIR *dir; unsigned char *data = get_uri_string(uri, URI_DATA); unsigned char dircolor[8] = ""; if (!data) fsp_error(connection_state(S_OUT_OF_MEM)); decode_uri(data); if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) fsp_error(connection_state(S_OUT_OF_MEM)); dir = fsp_opendir(ses, data); if (!dir) fsp_error(connection_state_for_errno(errno)); fprintf(stderr, "text/html"); fclose(stderr); puts(buf.source); if (get_opt_bool("document.browse.links.color_dirs", NULL)) { color_to_string(get_opt_color("document.colors.dirs", NULL), dircolor); } sort_and_display_entries(dir, dircolor); fsp_closedir(dir); puts("</pre><hr/></body></html>"); fsp_close_session(ses); exit(0); }
static void smb_got_data(struct socket *socket, struct read_buffer *rb) { int len = rb->length; struct connection *conn = socket->conn; if (len < 0) { abort_connection(conn, connection_state_for_errno(errno)); return; } if (!len) { abort_connection(conn, connection_state(S_OK)); return; } socket->state = SOCKET_END_ONCLOSE; conn->received += len; if (add_fragment(conn->cached, conn->from, rb->data, len) == 1) conn->tries = 0; conn->from += len; kill_buffer_data(rb, len); read_from_socket(socket, rb, connection_state(S_TRANS), smb_got_data); }
static struct uri * proxy_uri(struct uri *uri, unsigned char *proxy, struct connection_state *error_state) { struct string string; if (init_string(&string) && string_concat(&string, "proxy://", proxy, "/", (unsigned char *) NULL) && add_uri_to_string(&string, uri, URI_BASE)) { /* There is no need to use URI_BASE when calling get_uri() * because URI_BASE should not add any fragments in the first * place. */ uri = get_uri(string.source, 0); /* XXX: Assume the problem is due to @proxy having bad format. * This is a lot faster easier than checking the format. */ if (!uri) *error_state = connection_state(S_PROXY_ERROR); } else { uri = NULL; *error_state = connection_state(S_OUT_OF_MEM); } done_string(&string); return uri; }
/* DNS callback. */ static void dns_found(struct socket *socket, struct sockaddr_storage *addr, int addrlen) { struct connect_info *connect_info = socket->connect_info; int size; if (!addr) { socket->ops->done(socket, connection_state(S_NO_DNS)); return; } assert(connect_info); size = sizeof(*addr) * addrlen; connect_info->addr = mem_alloc(size); if (!connect_info->addr) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return; } memcpy(connect_info->addr, addr, size); connect_info->addrno = addrlen; /* XXX: Passing non-result state here is bad but a lack of alternatives * makes it so. Well adding get_state() socket operation could maybe fix * it but the returned state would most likely be a non-result one at * this point in the connection lifecycle. This will, however, only be a * problem if connect_socket() fails without doing any system calls * which is only the case when forcing the IP family. So it is better to * handle it in connect_socket(). */ connect_socket(socket, connection_state(S_CONN)); }
/* Returns a connection state. S_OK if all is well. */ static inline struct connection_state list_directory(struct connection *conn, unsigned char *dirpath, struct string *page) { int show_hidden_files = get_opt_bool((const unsigned char *)"protocol.file.show_hidden_files", NULL); struct directory_entry *entries; struct connection_state state; errno = 0; entries = get_directory_entries(dirpath, show_hidden_files); if (!entries) { if (errno) return connection_state_for_errno(errno); return connection_state(S_OUT_OF_MEM); } state = init_directory_listing(page, conn->uri); if (!is_in_state(state, S_OK)) return connection_state(S_OUT_OF_MEM); add_dir_entries(entries, dirpath, page); if (!add_to_string(page, (const unsigned char *)"</pre>\n<hr/>\n</body>\n</html>\n")) { done_string(page); return connection_state(S_OUT_OF_MEM); } return connection_state(S_OK); }
/** @return -2 if no data was read but the caller should retry; * -1 if an error occurred and *@a error was set; 0 at end of data; * a positive number if that many bytes were read. * * @relates http_post */ static int read_http_post_fd(struct http_post *http_post, unsigned char buffer[], int max, struct connection_state *error) { const struct http_post_file *const file = &http_post->files[http_post->file_index]; int ret; /* safe_read() would set errno = EBADF anyway, but check this * explicitly to make any such bugs easier to detect. */ assert(http_post->post_fd >= 0); if_assert_failed { *error = connection_state(S_INTERNAL); return -1; } ret = safe_read(http_post->post_fd, buffer, max); if (ret <= 0) { const int errno_from_read = errno; close(http_post->post_fd); http_post->post_fd = -1; http_post->file_index++; /* http_post->file_read is used below so don't clear it here. * It will be cleared when the next file is opened. */ if (ret == -1) { *error = connection_state_for_errno(errno_from_read); return -1; } else if (http_post->file_read != file->size) { /* ELinks already sent a Content-Length header * based on the size of this file, but the * file has since been shrunk. Abort the * connection because ELinks can no longer get * enough data to fill the Content-Length. * (Well, it could pad with zeroes, but that * would be just weird.) */ *error = connection_state(S_HTTP_UPLOAD_RESIZED); return -1; } else { /* The upload file ended but there may still * be more data in uri.post. If not, * read_http_post_inline() will return 0 to * indicate the final end of file. */ return -2; } } http_post->file_read += ret; if (http_post->file_read > file->size) { /* ELinks already sent a Content-Length header based * on the size of this file, but the file has since * been extended. Abort the connection because ELinks * can no longer fit the entire file in the original * Content-Length. */ *error = connection_state(S_HTTP_UPLOAD_RESIZED); return -1; } return ret; }
void data_protocol_handler(struct connection *conn) { struct uri *uri = conn->uri; struct cache_entry *cached = get_cache_entry(uri); unsigned char *data_start, *data; int base64 = 0; if (!cached) { abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } conn->cached = cached; data_start = parse_data_protocol_header(conn, &base64); if (!data_start) { abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } /* Allocate the data string because URI decoding will possibly modify * it. */ data = memacpy(data_start, uri->datalen - (data_start - uri->data)); if (!data) { abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } if (base64) { unsigned char *decoded = base64_encode(data); if (!decoded) { abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } mem_free_set(&data, decoded); } else { decode_uri(data); } { /* Use strlen() to get the correct decoded length */ int datalen = strlen((const char *)data); add_fragment(cached, conn->from, data, datalen); conn->from += datalen; } mem_free(data); abort_connection(conn, connection_state(S_OK)); }
static struct connection_state init_nntp_header(struct connection *conn, struct read_buffer *rb) { struct nntp_connection_info *nntp = conn->info; if (!conn->cached) { conn->cached = get_cache_entry(conn->uri); if (!conn->cached) return connection_state(S_OUT_OF_MEM); } else if (conn->cached->head || conn->cached->content_type) { /* If the head is set wipe out the content to be sure */ delete_entry_content(conn->cached); mem_free_set(&conn->cached->head, NULL); } /* XXX: Override any Content-Type line in the header */ mem_free_set(&conn->cached->content_type, stracpy("text/html")); if (!conn->cached->content_type) return connection_state(S_OUT_OF_MEM); switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: { unsigned char *end; end = get_nntp_message_header_end(rb->data, rb->length); if (!end) { /* Redo the whole cache entry thing next time */ return connection_state(S_TRANS); } /* FIXME: Add the NNTP response code line */ conn->cached->head = stracpy("FIXME NNTP response code\r\n"); if (!conn->cached->head) return connection_state(S_OUT_OF_MEM); add_to_strn(&conn->cached->head, rb->data); /* ... and remove it */ conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); break; } case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: case NNTP_TARGET_QUIT: break; } return connection_state(S_OK); }
void make_connection(struct socket *socket, struct uri *uri, socket_connect_T connect_done, int no_cache) { unsigned char *host = get_uri_string(uri, URI_DNS_HOST); struct connect_info *connect_info; enum dns_result result; enum blacklist_flags verify; socket->ops->set_timeout(socket, connection_state(0)); if (!host) { socket->ops->retry(socket, connection_state(S_OUT_OF_MEM)); return; } connect_info = init_connection_info(uri, socket, connect_done); if (!connect_info) { mem_free(host); socket->ops->retry(socket, connection_state(S_OUT_OF_MEM)); return; } socket->connect_info = connect_info; /* XXX: Keep here and not in init_connection_info() to make * complete_connect_socket() work from the HTTP implementation. */ socket->need_ssl = get_protocol_need_ssl(uri->protocol); if (!socket->set_no_tls) { enum blacklist_flags flags = get_blacklist_flags(uri); socket->no_tls = ((flags & SERVER_BLACKLIST_NO_TLS) != 0); socket->set_no_tls = 1; } verify = get_blacklist_flags(uri); socket->verify = ((verify & SERVER_BLACKLIST_NO_CERT_VERIFY) == 0); debug_transfer_log("\nCONNECTION: ", -1); debug_transfer_log(host, -1); debug_transfer_log("\n", -1); result = find_host(host, &connect_info->dnsquery, (dns_callback_T) dns_found, socket, no_cache); mem_free(host); if (result == DNS_ASYNC) socket->ops->set_state(socket, connection_state(S_DNS)); }
/* Called when we receive a connection on the listening socket. */ static void accept_bittorrent_peer_connection(void *____) { struct sockaddr_in addr; int peer_sock; int addrlen = sizeof(addr); struct bittorrent_peer_connection *peer; struct read_buffer *buffer; peer_sock = accept(bittorrent_socket, (struct sockaddr *) &addr, &addrlen); if (peer_sock < 0) return; if (set_nonblocking_fd(peer_sock) < 0) { close(peer_sock); return; } peer = init_bittorrent_peer_connection(peer_sock); if (!peer) { close(peer_sock); return; } peer->remote.initiater = 1; /* Just return. Failure is handled by alloc_read_buffer(). */ buffer = alloc_read_buffer(peer->socket); if (!buffer) return; read_from_socket(peer->socket, buffer, connection_state(S_TRANS), read_bittorrent_peer_handshake); add_to_list(bittorrent_peer_connections, peer); }
void about_protocol_handler(struct connection *conn) { struct cache_entry *cached = get_cache_entry(conn->uri); /* Only do this the first time */ if (cached && !cached->content_type) { #ifndef CONFIG_SMALL { const struct about_page *page = about_pages; for (; page->name; page++) { int len; unsigned char *str; if (strcmp(conn->uri->data, page->name)) continue; str = page->string; len = strlen(str); add_fragment(cached, 0, str, len); conn->from = len; break; } } #endif /* Set content to known type */ mem_free_set(&cached->content_type, stracpy("text/html")); } conn->cached = cached; abort_connection(conn, connection_state(S_OK)); }
static void read_response_from_socket(struct socket *socket) { struct read_buffer *rb = alloc_read_buffer(socket); if (rb) read_from_socket(socket, rb, connection_state(S_SENT), socket->read_done); }
/* Called when the connection changes state. Usually state starts out being * S_DMS (while looking up the host) then moves to S_CONN (while connecting), * and should hopefully become S_TRANS (while transfering). Note, state can hold * both internally defined connection states as described above and errno * values, such as ECONNREFUSED. */ static void set_bittorrent_socket_state(struct socket *socket, struct connection_state state) { struct bittorrent_peer_connection *peer = socket->conn; if (is_in_state(state, S_TRANS) && peer->bittorrent) set_connection_state(peer->bittorrent->conn, connection_state(S_TRANS)); }
static void check_if_closed(struct socket *socket, struct read_buffer *rb) { if (socket->state == SOCKET_CLOSED) { abort_connection((struct connection *)socket->conn, connection_state(S_OK)); return; } http_got_header(socket, rb); }
static void smb_got_error(struct socket *socket, struct read_buffer *rb) { int len = rb->length; struct connection *conn = socket->conn; struct connection_state error; if (len < 0) { abort_connection(conn, connection_state_for_errno(errno)); return; } /* There should be free space in the buffer, because * @alloc_read_buffer allocated several kibibytes, and the * child process wrote only an integer and a newline to the * pipe. */ assert(rb->freespace >= 1); if_assert_failed { abort_connection(conn, connection_state(S_INTERNAL)); return; } rb->data[len] = '\0'; switch (rb->data[0]) { case 'S': error = connection_state_for_errno(atoi(rb->data + 1)); break; case 'I': error = connection_state(atoi(rb->data + 1)); break; default: ERROR("malformed error code: %s", rb->data); error = connection_state(S_INTERNAL); break; } kill_buffer_data(rb, len); if (is_system_error(error) && error.syserr == EACCES) prompt_username_pw(conn); else abort_connection(conn, error); }
/** @return -2 if no data was read but the caller should retry; * -1 if an error occurred and *@a error was set; 0 at end of data; * a positive number if that many bytes were read. * * @relates http_post */ static int read_http_post_inline(struct http_post *http_post, unsigned char buffer[], int max, struct connection_state *error) { const unsigned char *post = http_post->post_data; const unsigned char *end = (const unsigned char *)strchr((char *)post, FILE_CHAR); int total = 0; assert(http_post->post_fd < 0); if_assert_failed { *error = connection_state(S_INTERNAL); return -1; } if (!end) end = (const unsigned char *)strchr((char *)post, '\0'); while (post < end && total < max) { int h1, h2; h1 = unhx(post[0]); assertm(h1 >= 0 && h1 < 16, "h1 in the POST buffer is %d (%d/%c)", h1, post[0], post[0]); if_assert_failed h1 = 0; h2 = unhx(post[1]); assertm(h2 >= 0 && h2 < 16, "h2 in the POST buffer is %d (%d/%c)", h2, post[1], post[1]); if_assert_failed h2 = 0; buffer[total++] = (h1<<4) + h2; post += 2; } if (post != end || *end != FILE_CHAR) { http_post->post_data = post; return total; } http_post->file_read = 0; end = (const unsigned char *)strchr((char *)(post + 1), FILE_CHAR); assert(end); http_post->post_fd = open((const char *)http_post->files[http_post->file_index].name, O_RDONLY); /* Be careful not to change errno here. */ if (http_post->post_fd < 0) { http_post->post_data = post; if (total > 0) return total; /* retry the open on the next call */ else { *error = connection_state_for_errno(errno); return -1; } } http_post->post_data = end + 1; return total ? total : -2; }
void timeout_socket(struct socket *socket) { if (!socket->connect_info) { socket->ops->retry(socket, connection_state(S_TIMEOUT)); return; } /* Is the DNS resolving still in progress? */ if (socket->connect_info->dnsquery) { socket->ops->done(socket, connection_state(S_TIMEOUT)); return; } /* Try the next address, */ connect_socket(socket, connection_state(S_TIMEOUT)); /* Reset the timeout if connect_socket() started a new attempt * to connect. */ if (socket->connect_info) socket->ops->set_timeout(socket, connection_state(0)); }
struct read_buffer * alloc_read_buffer(struct socket *socket) { struct read_buffer *rb; rb = mem_calloc(1, RD_SIZE(rb, 0)); if (!rb) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return NULL; } rb->freespace = RD_SIZE(rb, 0) - sizeof(*rb); return rb; }
void write_to_socket(struct socket *socket, unsigned char *data, int len, struct connection_state state, socket_write_T write_done) { select_handler_T read_handler; struct write_buffer *wb; debug_transfer_log(data, len); assert(len > 0); if_assert_failed return; socket->ops->set_timeout(socket, connection_state(0)); wb = mem_alloc(sizeof(*wb) + len); if (!wb) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return; } wb->length = len; wb->pos = 0; wb->done = write_done; memcpy(wb->data, data, len); mem_free_set(&socket->write_buffer, wb); if (socket->duplex) { read_handler = get_handler(socket->fd, SELECT_HANDLER_READ); } else { read_handler = NULL; } set_handlers(socket->fd, read_handler, (select_handler_T) write_select, (select_handler_T) exception, socket); socket->ops->set_state(socket, state); }
void complete_connect_socket(struct socket *socket, struct uri *uri, socket_connect_T done) { struct connect_info *connect_info = socket->connect_info; if (connect_info && connect_info->uri) { /* Remember whether the server supported TLS or not. * Then the next request can immediately use the right * protocol. This is important for HTTP POST requests * because it is not safe to silently retry them. The * uri parameter is normally NULL here so don't use it. */ if (socket->no_tls) add_blacklist_entry(connect_info->uri, SERVER_BLACKLIST_NO_TLS); else del_blacklist_entry(connect_info->uri, SERVER_BLACKLIST_NO_TLS); } /* This is a special case used by the HTTP implementation to acquire an * SSL link for handling CONNECT requests. */ if (!connect_info) { assert(uri && socket); connect_info = init_connection_info(uri, socket, done); if (!connect_info) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return; } socket->connect_info = connect_info; } #ifdef CONFIG_SSL /* Check if the connection should run over an encrypted link */ if (socket->need_ssl && !socket->ssl && ssl_connect(socket) < 0) return; #endif if (connect_info->done) connect_info->done(socket); done_connection_info(socket); }
static void get_request(struct connection *conn) { struct read_buffer *rb = alloc_read_buffer(conn->socket); if (!rb) return; memcpy(rb->data, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n", 45); rb->length = 45; rb->freespace -= 45; conn->unrestartable = 1; conn->socket->state = SOCKET_END_ONCLOSE; read_from_socket(conn->socket, rb, connection_state(S_SENT), http_got_header); }
static void read_from_stdin(struct connection *conn) { struct read_buffer *rb = alloc_read_buffer(conn->socket); if (!rb) return; memcpy(rb->data, "HTTP/1.0 200 OK\r\n\r\n", 19); rb->length = 19; rb->freespace -= 19; conn->unrestartable = 1; conn->socket->state = SOCKET_END_ONCLOSE; read_from_socket(conn->socket, rb, connection_state(S_SENT), check_if_closed); }
struct connection_state read_nntp_response_data(struct connection *conn, struct read_buffer *rb) { struct string html; unsigned char *end; struct connection_state state = connection_state(S_TRANS); if (conn->from == 0) { switch (init_nntp_header(conn, rb).basic) { case S_OK: break; case S_OUT_OF_MEM: return connection_state(S_OUT_OF_MEM); case S_TRANS: return connection_state(S_TRANS); default: return connection_state(S_NNTP_ERROR); } } if (!init_string(&html)) return connection_state(S_OUT_OF_MEM); if (conn->from == 0) add_nntp_html_start(&html, conn); while ((end = get_nntp_line_end(rb->data, rb->length))) { unsigned char *line = check_nntp_line(rb->data, end); if (!line) { state = connection_state(S_OK); break; } add_nntp_html_line(&html, conn, line); conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); } if (!is_in_state(state, S_TRANS)) add_nntp_html_end(&html, conn); add_fragment(conn->cached, conn->from, html.source, html.length); conn->from += html.length; done_string(&html); return state; }
static void smb_directory(int dir, struct string *prefix, struct uri *uri) { struct string buf; struct directory_entry *entries; if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) { smb_error(connection_state(S_OUT_OF_MEM)); } fputs("text/html", header_out); fclose(header_out); entries = get_smb_directory_entries(dir, prefix); add_smb_dir_entries(entries, NULL, &buf); add_to_string(&buf, "</pre><hr/></body></html>\n"); fputs(buf.source, data_out); done_string(&buf); exit(0); }
void timeout_socket( struct socket *socket ) { int eax; if ( socket->connect_info == 0 ) { connection_state( S_TIMEOUT ); esi( socket[0].fd ); return; } else { if ( socket->connect_info->dnsquery ) { if ( assert_failed == 0 ) assert_failed = 0; eax( socket[0].fd, (long long)-100010 ); return; } else { if ( assert_failed == 0 ) assert_failed = 0; else assert_failed = 0; connect_socket( &socket[0], (long long)-100003 ); if ( socket->connect_info ) { if ( assert_failed == 0 ) assert_failed = 0; } else { return; } } assert_failed = 0; } }
void read_from_socket(struct socket *socket, struct read_buffer *buffer, struct connection_state state, socket_read_T done) { const int is_buffer_new = (buffer != socket->read_buffer); struct socket_weak_ref ref; select_handler_T write_handler; ref.socket = socket; add_to_list(socket_weak_refs, &ref); buffer->done = done; socket->ops->set_timeout(socket, connection_state(0)); socket->ops->set_state(socket, state); del_from_list(&ref); if (ref.socket == NULL) { /* socket->ops->set_state deleted the socket. */ if (is_buffer_new) mem_free(buffer); return; } if (socket->read_buffer && buffer != socket->read_buffer) mem_free(socket->read_buffer); socket->read_buffer = buffer; if (socket->duplex) { write_handler = get_handler(socket->fd, SELECT_HANDLER_WRITE); } else { write_handler = NULL; } set_handlers(socket->fd, (select_handler_T) read_select, write_handler, (select_handler_T) exception, socket); }
void mailcap_protocol_handler(struct connection *conn) { #ifdef HAVE_FORK unsigned char *script, *ref; pid_t pid; struct connection_state state = connection_state(S_OK); int pipe_read[2], check; /* security checks */ if (!conn->referrer || conn->referrer->protocol != PROTOCOL_MAILCAP) { goto bad; } ref = get_uri_string(conn->referrer, URI_DATA); if (!ref) { goto bad; } check = strcmp(ref, "elmailcap"); mem_free(ref); if (check) goto bad; script = get_uri_string(conn->uri, URI_DATA); if (!script) { state = connection_state(S_OUT_OF_MEM); goto end2; } if (c_pipe(pipe_read)) { state = connection_state_for_errno(errno); goto end1; } pid = fork(); if (pid < 0) { state = connection_state_for_errno(errno); goto end0; } if (!pid) { if (dup2(pipe_read[1], STDOUT_FILENO) < 0) { _exit(2); } /* We implicitly chain stderr to ELinks' stderr. */ close_all_non_term_fd(); if (execl("/bin/sh", "/bin/sh", "-c", script, (char *) NULL)) { _exit(3); } } else { /* ELinks */ mem_free(script); if (!init_http_connection_info(conn, 1, 0, 1)) { close(pipe_read[0]); close(pipe_read[1]); return; } close(pipe_read[1]); conn->socket->fd = pipe_read[0]; conn->data_socket->fd = -1; conn->cgi = 1; set_nonblocking_fd(conn->socket->fd); get_request(conn); return; } end0: close(pipe_read[0]); close(pipe_read[1]); end1: mem_free(script); end2: abort_connection(conn, state); return; #endif bad: abort_connection(conn, connection_state(S_BAD_URL)); }
void smb_protocol_handler(struct connection *conn) { int smb_pipe[2] = { -1, -1 }; int header_pipe[2] = { -1, -1 }; pid_t cpid; if (c_pipe(smb_pipe) || c_pipe(header_pipe)) { int s_errno = errno; if (smb_pipe[0] >= 0) close(smb_pipe[0]); if (smb_pipe[1] >= 0) close(smb_pipe[1]); if (header_pipe[0] >= 0) close(header_pipe[0]); if (header_pipe[1] >= 0) close(header_pipe[1]); abort_connection(conn, connection_state_for_errno(s_errno)); return; } conn->from = 0; conn->unrestartable = 1; find_auth(conn->uri); /* remember username and password */ cpid = fork(); if (cpid == -1) { int s_errno = errno; close(smb_pipe[0]); close(smb_pipe[1]); close(header_pipe[0]); close(header_pipe[1]); retry_connection(conn, connection_state_for_errno(s_errno)); return; } if (!cpid) { dup2(open("/dev/null", O_RDONLY), 0); close(1); close(2); data_out = fdopen(smb_pipe[1], "w"); header_out = fdopen(header_pipe[1], "w"); if (!data_out || !header_out) exit(1); close(smb_pipe[0]); close(header_pipe[0]); /* There may be outgoing data in stdio buffers * inherited from the parent process. The parent * process is going to write this data, so the child * process must not do that. Closing the file * descriptors ensures this. * * FIXME: If something opens more files and gets the * same file descriptors and does not close them * before exit(), then stdio may attempt to write the * buffers to the wrong files. This might happen for * example if libsmbclient calls syslog(). */ close_all_fds_but_two(smb_pipe[1], header_pipe[1]); do_smb(conn); } else { struct read_buffer *buf2; conn->data_socket->fd = smb_pipe[0]; conn->socket->fd = header_pipe[0]; set_nonblocking_fd(conn->data_socket->fd); set_nonblocking_fd(conn->socket->fd); close(smb_pipe[1]); close(header_pipe[1]); buf2 = alloc_read_buffer(conn->socket); if (!buf2) { close_socket(conn->data_socket); close_socket(conn->socket); abort_connection(conn, connection_state(S_OUT_OF_MEM)); return; } read_from_socket(conn->socket, buf2, connection_state(S_CONN), smb_got_header); } }