/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = arg; CQ_ITEM *item; char buf[1]; //响应pipe可读事件,读取主线程向管道内写的1字节数据(见dispatch_conn_new()函数) if (read(fd, buf, 1) != 1) if (settings.verbose > 0) fprintf(stderr, "Can't read from libevent pipe\n"); switch (buf[0]) { case 'c': //从链接队列中取出一个conn item = cq_pop(me->new_conn_queue); if (NULL != item) { //使用conn创建新的任务,并注册事件 conn *c = conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base); if (c == NULL) { if (IS_UDP(item->transport)) { fprintf(stderr, "Can't listen for events on UDP socket\n"); exit(1); } else { if (settings.verbose > 0) { fprintf(stderr, "Can't listen for events on fd %d\n", item->sfd); } close(item->sfd); } } else { c->thread = me; } cqi_free(item); } break; /* we were told to pause and report in */ case 'p': register_thread_initialized(); break; } }
void conn_accept (int sockfd, short event, void* arg) { if(event == EV_TIMEOUT) { g_print("%d :accept timeout %d\n", sockfd, event); return; } g_print("%d :accept %d\n", sockfd, event); struct sockaddr_in cliaddr; g_print("%s(): fd = %d, event = %d.\n", __func__, sockfd, event); socklen_t len = sizeof (struct sockaddr_in); /* Accept the new connection. */ int connfd = accept (sockfd, (struct sockaddr*)&cliaddr, &len); if (connfd < 0) { g_print("accept failed! (%s)\n", strerror(errno)); return; } connection_t *conn = conn_new(); conn->sockfd = connfd; conn->tv.tv_sec = 5; //timeout_set(&conn->ev_read, timeout_cb, conn); event_set(&conn->ev_read, connfd, EV_READ, conn_input, conn); if (event_add (&conn->ev_read, NULL) == -1) { return; } timeout_add(&conn->ev_read, &conn->tv); g_print("Accepted connection from %s, socket=%d\n", inet_ntoa(cliaddr.sin_addr), connfd); }
/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = (LIBEVENT_THREAD *)arg; CQ_ITEM *item; char buf[1]; if (read(fd, buf, 1) != 1) if (g_settings.verbose > 0) { log_debug(LOG_ERR, "Can't read from libevent pipe\n"); } if (buf[0] != 'c') { return; } item = cq_pop(me->new_conn_queue); if (NULL != item) { conn *c = conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base); if (c == NULL) { if (g_settings.verbose > 0) { log_debug(LOG_ERR, "Can't listen for events on fd %d\n", item->sfd); } close(item->sfd); } else { c->thread = me; } cqi_free(item); } }
static void sig_usr1( int signo ) { struct stat statbuf; work_info_t wi; conn_t *con; traceLog(LOG_NOTICE, "Received SIGUSR1"); stat( srv.rulefile, &statbuf ); if (statbuf.st_mtime != srv.mtime ) { srv.mtime = statbuf.st_mtime; con = conn_new(); con->srv = &srv; con->rs = srv.root; memset( &wi, 0, sizeof( work_info_t )); wi.conn = con; wi.routine = &do_reread_rulefile; reread_rulefile = 1; tpool_add_work( srv.work, &wi); } }
void http_accept(IO *io, int events, void *arg) { int cfd, fd = io_get_fd(io); struct sockaddr_in addr = { 0 }; size_t addr_len = sizeof(addr); IO *client; struct service *service = arg; if (events & IO_IN) { if ((cfd = accept(fd, (struct sockaddr *)&addr, &addr_len)) > -1) { socket_set_nonblock(cfd); client = io_new_fd(cfd); /* TODO: create conn */ struct conn *conn = conn_new(service, client); printf("--> accepted\n"); io_watch(client, IO_IN | IO_HUP, http_incoming, conn); } } }
static void listener_cb(struct evconnlistener *listener, evutil_socket_t socket, struct sockaddr *sockaddr, int socklen, void *userdata) { DEBUG("New connection."); ad_server_t *server = (ad_server_t *)userdata; // Create a new buffer. struct bufferevent *buffer = NULL; if (server->sslctx) { buffer = bufferevent_openssl_socket_new(server->evbase, socket, SSL_new(server->sslctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE); } else { buffer = bufferevent_socket_new(server->evbase, socket, BEV_OPT_CLOSE_ON_FREE); } if (buffer == NULL) goto error; // Set read timeout. int timeout = ad_server_get_option_int(server, "server.timeout"); if (timeout > 0) { struct timeval tm; bzero((void *)&tm, sizeof(struct timeval)); tm.tv_sec = timeout; bufferevent_set_timeouts(buffer, &tm, NULL); } // Create a connection. void *conn = conn_new(server, buffer); if (! conn) goto error; return; error: if (buffer) bufferevent_free(buffer); ERROR("Failed to create a connection handler."); event_base_loopbreak(server->evbase); server->errcode = ENOMEM; }
static void thread_libevent_process(int fd, short which, void *arg) { thread_cg *me = arg; char buf[1]; CQ_ITEM *item; if (read(fd, buf, 1) != 1) { perror("thread_libevent_process read error"); exit(1); } switch(buf[0]){ case 'c': //从连接管理中,pop出来一个连接,将连接(conn_new)注册到event_handler来处理 item = cq_pop(me->new_conn_queue); if (NULL != item) { conn *c = conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base); if (c == NULL) { if (IS_UDP(item->transport)) { fprintf(stderr, "Can't listen for events on UDP socket\n"); exit(1); } else { fprintf(stderr, "Can't listen for events on fd %d\n", item->sfd); close(item->sfd); } } else { c->thread = me; } cqi_free(item); } break; case 'p': break; } }
void on_nble_gap_connect_evt(const struct nble_gap_connect_evt *ev) { struct bt_conn *conn; BT_DBG("handle %u role %u", ev->conn_handle, ev->role_slave); conn = conn_new(); if (!conn) { BT_ERR("Unable to create new bt_conn object"); return; } conn->handle = ev->conn_handle; conn->role = ev->role_slave; conn->interval = ev->conn_values.interval; conn->latency = ev->conn_values.latency; conn->timeout = ev->conn_values.supervision_to; bt_addr_le_copy(&conn->dst, &ev->peer_bda); conn->state = BT_CONN_CONNECTED; notify_connected(conn); }
/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = arg; CQ_ITEM *cq_item; char buf[1]; (void)which; if (read(fd, buf, 1) != 1) if (settings.verbose > 0) moxi_log_write("Can't read from libevent pipe\n"); cq_item = cq_pop(me->new_conn_queue); if (NULL != cq_item) { conn *c = conn_new(cq_item->sfd, cq_item->init_state, cq_item->event_flags, cq_item->read_buffer_size, cq_item->transport, me->base, cq_item->funcs, cq_item->extra); if (c == NULL) { if (IS_UDP(cq_item->transport)) { moxi_log_write("Can't listen for events on UDP socket\n"); exit(1); } else { if (settings.verbose > 0) { moxi_log_write("Can't listen for events on fd %d\n", cq_item->sfd); } close(cq_item->sfd); } } else { c->protocol = cq_item->protocol; c->thread = me; } cqi_free(cq_item); } }
static void create_conn (Sess *sess, struct Conn_Info *ci) { Conn_Private_Data *cpriv; /* No connection yet (or anymore). Create a new connection. Note that CI->CONN is NOT reference-counted. This is again to avoid introducing recursive dependencies (see also comment regarding member CONN in call.h). */ ci->conn = conn_new (); if (!ci->conn) { sess_failure (sess); return; } cpriv = CONN_PRIVATE_DATA (ci->conn); cpriv->sess = sess; cpriv->ci = ci; ci->is_connected = 0; ci->is_successful = 0; ci->num_sent = 0; /* (re-)send all pending calls */ #ifdef HAVE_SSL if (param.ssl_reuse && ci->conn->ssl && sess->ssl) { if (DBG > 0) fprintf (stderr, "create_conn: reusing SSL session %p\n", (void *) sess->ssl); SSL_copy_session_id (ci->conn->ssl, sess->ssl); } #endif if (core_connect (ci->conn) < 0) sess_failure (sess); }
void accept_handler(int fd, short ev_type, void *data) { int newfd; int res; struct sockaddr_in addr; socklen_t addr_len; char buf[1024]; size_t nb; conn_t *conn; newfd = accept(fd, (struct sockaddr *)&addr, &addr_len); if (newfd < 0) { /* another process beat us to the accept? */ return; } fcntl(newfd, F_SETFL, O_NONBLOCK); conn = conn_new(); conn->fd = newfd; if (req_init_inbound(conn) < 0) { conn_free(conn); } }
int main(int argc, char *argv[]) { static char *cmdstr = NULL; struct cmd_set **cmd_set; static struct vfs_state vfs; int i; static char *filename = NULL; /* make sure the vars that get altered (4th field) are in a fixed location or certain compilers complain */ poptContext pc; struct poptOption long_options[] = { POPT_AUTOHELP {"file", 'f', POPT_ARG_STRING, &filename, 0, }, {"command", 'c', POPT_ARG_STRING, &cmdstr, 0, "Execute specified list of commands" }, POPT_COMMON_SAMBA POPT_TABLEEND }; setlinebuf(stdout); pc = poptGetContext("vfstest", argc, (const char **) argv, long_options, 0); while(poptGetNextOpt(pc) != -1); poptFreeContext(pc); /* TODO: check output */ reload_services(False); /* the following functions are part of the Samba debugging facilities. See lib/debug.c */ setup_logging("vfstest", True); /* Load command lists */ cmd_set = vfstest_command_list; while(*cmd_set) { add_command_set(*cmd_set); add_command_set(separator_command); cmd_set++; } /* some basic initialization stuff */ sec_init(); conn_init(); vfs.conn = conn_new(); string_set(&vfs.conn->user,"vfstest"); for (i=0; i < 1024; i++) vfs.files[i] = NULL; /* some advanced initiliazation stuff */ smbd_vfs_init(vfs.conn); /* Do we have a file input? */ if (filename && filename[0]) { process_file(&vfs, filename); return 0; } /* Do anything specified with -c */ if (cmdstr && cmdstr[0]) { char *cmd; char *p = cmdstr; while((cmd=next_command(&p)) != NULL) { process_cmd(&vfs, cmd); } return 0; } /* Loop around accepting commands */ while(1) { pstring prompt; char *line; slprintf(prompt, sizeof(prompt) - 1, "vfstest $> "); line = smb_readline(prompt, NULL, completion_fn); if (line == NULL) break; if (line[0] != '\n') process_cmd(&vfs, line); } free(vfs.conn); return 0; }
int server_socket(const char *interface, int port, int backlog) { int sfd; struct linger ling; struct addrinfo *ai; struct addrinfo *next; struct addrinfo hints; char port_buf[NI_MAXSERV]; int error; int success = 0; int flags = 1; ling.l_onoff = 0; ling.l_linger = 0; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = base_conf.support_ipv6 ? AF_UNSPEC : AF_INET; hints.ai_socktype = SOCK_STREAM; if (port == -1) { port = 0; } snprintf(port_buf, sizeof(port_buf), "%d", port); error= getaddrinfo(interface, port_buf, &hints, &ai); if (error != 0) { if (error != EAI_SYSTEM) fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); else perror("getaddrinfo()"); return 1; } for (next= ai; next; next= next->ai_next) { conn *listen_conn_add; if ((sfd = new_socket(next)) == -1) { close(sfd); freeaddrinfo(ai); return 1; } #ifdef IPV6_V6ONLY if (next->ai_family == AF_INET6) { error = setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags)); if (error != 0) { perror("setsockopt"); close(sfd); continue; } } #endif setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); if (error != 0) perror("setsockopt"); error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); if (error != 0) perror("setsockopt"); error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); if (error != 0) perror("setsockopt"); if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) { perror("bind()"); close(sfd); freeaddrinfo(ai); return 1; } else { success++; if (listen(sfd, backlog) == -1) { perror("listen()"); close(sfd); freeaddrinfo(ai); return 1; } } if (!(listen_conn_add = conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, get_main_thread()))) { fprintf(stderr, "failed to create listening connection\n"); exit(EXIT_FAILURE); } listen_conn_add->host->assign(interface?interface:"0.0.0.0"); listen_conn_add->port = port; listen_conn_add->next = listen_conn; listen_conn = listen_conn_add; } freeaddrinfo(ai); return success == 0; }
void drive_machine(conn *c) { int exit = 0; int sfd, flags = 1; socklen_t addrlen; struct sockaddr addr; conn *newc; int res; while (!exit) { /* printf("state %d\n", c->state);*/ switch(c->state) { case conn_listening: addrlen = sizeof(addr); if ((sfd = accept(c->sfd, &addr, &addrlen)) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { exit = 1; break; } else { perror("accept()"); } break; } if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) { perror("setting O_NONBLOCK"); close(sfd); break; } newc = conn_new(sfd, conn_read, EV_READ | EV_PERSIST); if (!newc) { if (settings.verbose > 0) fprintf(stderr, "couldn't create new connection\n"); close(sfd); break; } break; case conn_read: if (try_read_command(c)) { continue; } if (try_read_network(c)) { continue; } /* we have no command line and no data to read from network */ if (!update_event(c, EV_READ | EV_PERSIST)) { if (settings.verbose > 0) fprintf(stderr, "Couldn't update event\n"); c->state = conn_closing; break; } exit = 1; break; case conn_nread: /* we are reading rlbytes into rcurr; */ if (c->rlbytes == 0) { complete_nread(c); break; } /* first check if we have leftovers in the conn_read buffer */ if (c->rbytes > 0) { int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes; memcpy(c->rcurr, c->rbuf, tocopy); c->rcurr += tocopy; c->rlbytes -= tocopy; if (c->rbytes > tocopy) { memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy); } c->rbytes -= tocopy; break; } /* now try reading from the socket */ res = read(c->sfd, c->rcurr, c->rlbytes); if (res > 0) { stats.bytes_read += res; c->rcurr += res; c->rlbytes -= res; break; } if (res == 0) { /* end of stream */ c->state = conn_closing; break; } if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { if (!update_event(c, EV_READ | EV_PERSIST)) { if (settings.verbose > 0) fprintf(stderr, "Couldn't update event\n"); c->state = conn_closing; break; } exit = 1; break; } /* otherwise we have a real error, on which we close the connection */ if (settings.verbose > 0) fprintf(stderr, "Failed to read, and not due to blocking\n"); c->state = conn_closing; break; case conn_swallow: /* we are reading sbytes and throwing them away */ if (c->sbytes == 0) { c->state = conn_read; break; } /* first check if we have leftovers in the conn_read buffer */ if (c->rbytes > 0) { int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes; c->sbytes -= tocopy; if (c->rbytes > tocopy) { memmove(c->rbuf, c->rbuf+tocopy, c->rbytes - tocopy); } c->rbytes -= tocopy; break; } /* now try reading from the socket */ res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize); if (res > 0) { stats.bytes_read += res; c->sbytes -= res; break; } if (res == 0) { /* end of stream */ c->state = conn_closing; break; } if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { if (!update_event(c, EV_READ | EV_PERSIST)) { if (settings.verbose > 0) fprintf(stderr, "Couldn't update event\n"); c->state = conn_closing; break; } exit = 1; break; } /* otherwise we have a real error, on which we close the connection */ if (settings.verbose > 0) fprintf(stderr, "Failed to read, and not due to blocking\n"); c->state = conn_closing; break; case conn_write: /* we are writing wbytes bytes starting from wcurr */ if (c->wbytes == 0) { if (c->write_and_free) { free(c->write_and_free); c->write_and_free = 0; } c->state = c->write_and_go; if (c->state == conn_read) set_cork(c, 0); break; } res = write(c->sfd, c->wcurr, c->wbytes); if (res > 0) { stats.bytes_written += res; c->wcurr += res; c->wbytes -= res; break; } if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { if (!update_event(c, EV_WRITE | EV_PERSIST)) { if (settings.verbose > 0) fprintf(stderr, "Couldn't update event\n"); c->state = conn_closing; break; } exit = 1; break; } /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK, we have a real error, on which we close the connection */ if (settings.verbose > 0) fprintf(stderr, "Failed to write, and not due to blocking\n"); c->state = conn_closing; break; case conn_mwrite: /* * we're writing ibytes bytes from iptr. iptr alternates between * ibuf, where we build a string "VALUE...", and ITEM_data(it) for the * current item. When we finish a chunk, we choose the next one using * ipart, which has the following semantics: 0 - start the loop, 1 - * we finished ibuf, go to current ITEM_data(it); 2 - we finished ITEM_data(it), * move to the next item and build its ibuf; 3 - we finished all items, * write "END". */ if (c->ibytes > 0) { res = write(c->sfd, c->iptr, c->ibytes); if (res > 0) { stats.bytes_written += res; c->iptr += res; c->ibytes -= res; break; } if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { if (!update_event(c, EV_WRITE | EV_PERSIST)) { if (settings.verbose > 0) fprintf(stderr, "Couldn't update event\n"); c->state = conn_closing; break; } exit = 1; break; } /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK, we have a real error, on which we close the connection */ if (settings.verbose > 0) fprintf(stderr, "Failed to write, and not due to blocking\n"); c->state = conn_closing; break; } else { item *it; /* we finished a chunk, decide what to do next */ switch (c->ipart) { case 1: it = *(c->icurr); assert((it->it_flags & ITEM_SLABBED) == 0); c->iptr = ITEM_data(it); c->ibytes = it->nbytes; c->ipart = 2; break; case 2: it = *(c->icurr); item_remove(it); c->ileft--; if (c->ileft <= 0) { c->ipart = 3; break; } else { c->icurr++; } /* FALL THROUGH */ case 0: it = *(c->icurr); assert((it->it_flags & ITEM_SLABBED) == 0); c->ibytes = sprintf(c->ibuf, "VALUE %s %u %u\r\n", ITEM_key(it), it->flags, it->nbytes - 2); if (settings.verbose > 1) fprintf(stderr, ">%d sending key %s\n", c->sfd, ITEM_key(it)); c->iptr = c->ibuf; c->ipart = 1; break; case 3: out_string(c, "END"); break; } } break; case conn_closing: conn_close(c); exit = 1; break; } } return; }
/* This function is called from the protocol layer accept() in order to * instanciate a new session on behalf of a given listener and frontend. It * returns a positive value upon success, 0 if the connection can be ignored, * or a negative value upon critical failure. The accepted file descriptor is * closed if we return <= 0. If no handshake is needed, it immediately tries * to instanciate a new stream. */ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr) { struct connection *cli_conn; struct proxy *p = l->frontend; struct session *sess; struct stream *strm; struct task *t; int ret; ret = -1; /* assume unrecoverable error by default */ if (unlikely((cli_conn = conn_new()) == NULL)) goto out_close; conn_prepare(cli_conn, l->proto, l->xprt); cli_conn->t.sock.fd = cfd; cli_conn->addr.from = *addr; cli_conn->flags |= CO_FL_ADDR_FROM_SET; cli_conn->target = &l->obj_type; cli_conn->proxy_netns = l->netns; conn_ctrl_init(cli_conn); /* wait for a PROXY protocol header */ if (l->options & LI_O_ACC_PROXY) { cli_conn->flags |= CO_FL_ACCEPT_PROXY; conn_sock_want_recv(cli_conn); } conn_data_want_recv(cli_conn); if (conn_xprt_init(cli_conn) < 0) goto out_free_conn; sess = session_new(p, l, &cli_conn->obj_type); if (!sess) goto out_free_conn; p->feconn++; /* This session was accepted, count it now */ if (p->feconn > p->fe_counters.conn_max) p->fe_counters.conn_max = p->feconn; proxy_inc_fe_conn_ctr(l, p); /* now evaluate the tcp-request layer4 rules. We only need a session * and no stream for these rules. */ if ((l->options & LI_O_TCP_RULES) && !tcp_exec_req_rules(sess)) { /* let's do a no-linger now to close with a single RST. */ setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); ret = 0; /* successful termination */ goto out_free_sess; } /* monitor-net and health mode are processed immediately after TCP * connection rules. This way it's possible to block them, but they * never use the lower data layers, they send directly over the socket, * as they were designed for. We first flush the socket receive buffer * in order to avoid emission of an RST by the system. We ignore any * error. */ if (unlikely((p->mode == PR_MODE_HEALTH) || ((l->options & LI_O_CHK_MONNET) && addr->ss_family == AF_INET && (((struct sockaddr_in *)addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr))) { /* we have 4 possibilities here : * - HTTP mode, from monitoring address => send "HTTP/1.0 200 OK" * - HEALTH mode with HTTP check => send "HTTP/1.0 200 OK" * - HEALTH mode without HTTP check => just send "OK" * - TCP mode from monitoring address => just close */ if (l->proto->drain) l->proto->drain(cfd); if (p->mode == PR_MODE_HTTP || (p->mode == PR_MODE_HEALTH && (p->options2 & PR_O2_CHK_ANY) == PR_O2_HTTP_CHK)) send(cfd, "HTTP/1.0 200 OK\r\n\r\n", 19, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); else if (p->mode == PR_MODE_HEALTH) send(cfd, "OK\n", 3, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); ret = 0; goto out_free_sess; } /* Adjust some socket options */ if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6) { setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_CLI_KA) setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_NOLING) fdtab[cfd].linger_risk = 1; #if defined(TCP_MAXSEG) if (l->maxseg < 0) { /* we just want to reduce the current MSS by that value */ int mss; socklen_t mss_len = sizeof(mss); if (getsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &mss_len) == 0) { mss += l->maxseg; /* remember, it's < 0 */ setsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)); } } #endif } if (global.tune.client_sndbuf) setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &global.tune.client_sndbuf, sizeof(global.tune.client_sndbuf)); if (global.tune.client_rcvbuf) setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &global.tune.client_rcvbuf, sizeof(global.tune.client_rcvbuf)); if (unlikely((t = task_new()) == NULL)) goto out_free_sess; t->context = sess; t->nice = l->nice; /* OK, now either we have a pending handshake to execute with and * then we must return to the I/O layer, or we can proceed with the * end of the stream initialization. In case of handshake, we also * set the I/O timeout to the frontend's client timeout. * * At this point we set the relation between sess/task/conn this way : * * orig -- sess <-- context * | | * v | * conn -- owner ---> task */ if (cli_conn->flags & CO_FL_HANDSHAKE) { conn_attach(cli_conn, t, &sess_conn_cb); t->process = session_expire_embryonic; t->expire = tick_add_ifset(now_ms, p->timeout.client); task_queue(t); cli_conn->flags |= CO_FL_INIT_DATA | CO_FL_WAKE_DATA; return 1; } /* OK let's complete stream initialization since there is no handshake */ cli_conn->flags |= CO_FL_CONNECTED; /* we want the connection handler to notify the stream interface about updates. */ cli_conn->flags |= CO_FL_WAKE_DATA; /* if logs require transport layer information, note it on the connection */ if (sess->fe->to_log & LW_XPRT) cli_conn->flags |= CO_FL_XPRT_TRACKED; session_count_new(sess); strm = stream_new(sess, t, &cli_conn->obj_type); if (!strm) goto out_free_task; strm->target = sess->listener->default_target; strm->req.analysers = sess->listener->analysers; return 1; out_free_task: task_free(t); out_free_sess: p->feconn--; session_free(sess); out_free_conn: cli_conn->flags &= ~CO_FL_XPRT_TRACKED; conn_xprt_close(cli_conn); conn_free(cli_conn); out_close: if (ret < 0 && l->xprt == &raw_sock && p->mode == PR_MODE_HTTP) { /* critical error, no more memory, try to emit a 500 response */ struct chunk *err_msg = &p->errmsg[HTTP_ERR_500]; if (!err_msg->str) err_msg = &http_err_chunks[HTTP_ERR_500]; send(cfd, err_msg->str, err_msg->len, MSG_DONTWAIT|MSG_NOSIGNAL); } if (fdtab[cfd].owner) fd_delete(cfd); else close(cfd); return ret; }
int main(int argc, char **argv) { int debug = 0, conftest = 0, nodaemon = 0; int i = 0; unsigned int clilen; struct sockaddr_in cliaddr; struct timeval start, end; char *cnfg = DEF_CNFG; char localhost[MAXNAMLEN + 1], path[MAXNAMLEN + 1]; FILE *pidfp; octet_t oct; ruleset_t *rs; /* * Who am I running as ? */ uname(&myname); /* * spocp_err = 0 ; */ memset(&srv, 0, sizeof(srv_t)); pthread_mutex_init(&(srv.mutex), NULL); pthread_mutex_init(&(srv.mlock), NULL); gethostname(localhost, MAXNAMLEN); #ifdef HAVE_GETDOMAINNAME getdomainname(path, MAXNAMLEN); #else { char *pos; if(pos = strstr(localhost, ".")) strncpy(path, pos+1, MAXNAMLEN); else strcpy(path, ""); } #endif if (0) printf("Domain: %s\n", path); srv.hostname = Strdup(localhost); /* * truncating input strings to reasonable length */ for (i = 0; i < argc; i++) if (strlen(argv[i]) > 512) argv[i][512] = '\0'; while ((i = getopt(argc, argv, "Dhrtf:d:")) != EOF) { switch (i) { case 'D': nodaemon = 1; break; case 'f': cnfg = Strdup(optarg); break; case 'd': debug = atoi(optarg); if (debug < 0) debug = 0; break; case 't': conftest = 1; break; case 'r': srv.readonly = 1; case 'h': default: fprintf(stderr, "Usage: %s [-t] ", argv[0]); fprintf(stderr, "[-f configfile] "); fprintf(stderr, "[-D] [-d debuglevel]\n"); exit(0); } } srv.root = ruleset_new(0); if (srv_init(&srv, cnfg) < 0) exit(1); if (srv.port && srv.uds) { fprintf(stderr, "Sorry are not allowed to listen on both a unix domain socket and a port\n"); exit(1); } if (srv.logfile) spocp_open_log(srv.logfile, debug); else if (debug) spocp_open_log(0, debug); if (srv.name){ localcontext = (char *) Calloc(strlen(srv.name) + strlen("//") + 1, sizeof(char)); /* Flawfinder: ignore */ sprintf(localcontext, "//%s", srv.name); } else { localcontext = (char *) Calloc(strlen(localhost) + strlen("//") + 1, sizeof(char)); /* Flawfinder: ignore */ sprintf(localcontext, "//%s", localhost); } /* * where I put the access rules for access to this server and its * rules */ snprintf(path, MAXNAMLEN, "%s/server", localcontext); oct_assign(&oct, path); if ((rs = ruleset_create(&oct, srv.root)) == 0) exit(1); rs->db = db_new(); /* * access rules for operations */ snprintf(path, MAXNAMLEN, "%s/operation", localcontext); oct_assign(&oct, path); if ((rs = ruleset_create(&oct, srv.root)) == 0) exit(1); rs->db = db_new(); LOG(SPOCP_INFO) { traceLog(LOG_INFO, "Local context: \"%s\"", localcontext); traceLog(LOG_INFO, "initializing backends"); if (srv.root->db) plugin_display(srv.plugin); } if (srv.plugin) { run_plugin_init(&srv); } if ( get_rules( &srv ) != SPOCP_SUCCESS ) exit(1); /*ruleset_tree( srv.root, 0);*/ /* If only testing configuration and rulefile this is as far as I go */ if (conftest) { traceLog(LOG_INFO,"Configuration was OK"); exit(0); } gettimeofday(&start, NULL); if (srv.port || srv.uds) { /* * stdin and stdout will not be used from here on, close to * save file descriptors */ fclose(stdin); fclose(stdout); #ifdef HAVE_SSL /* * ---------------------------------------------------------- */ /* * build our SSL context, whether it will ever be used or not */ /* * mutex'es for openSSL to use */ THREAD_setup(); if (srv.certificateFile && srv.privateKey && srv.caList) { traceLog(LOG_INFO,"Initializing the TLS/SSL environment"); if (!(srv.ctx = tls_init(&srv))) { return FALSE; } } /* * ---------------------------------------------------------- */ #endif #ifdef HAVE_SASL { int r = sasl_server_init(sasl_cb, "spocp"); if (r != SASL_OK) { traceLog( LOG_ERR, "Unable to initialized SASL library: %s", sasl_errstring(r, NULL, NULL)); return FALSE; } } #endif saci_init(); if( nodaemon == 0 ) { #ifdef HAVE_DAEMON if (daemon(1, 1) < 0) { fprintf(stderr, "couldn't go daemon\n"); exit(1); } #else daemon_init("spocp", 0); #endif } if (srv.pidfile) { /* * Write the PID file. */ pidfp = fopen(srv.pidfile, "w"); if (pidfp == (FILE *) 0) { fprintf(stderr, "Couldn't open pidfile \"%s\"\n", srv.pidfile); exit(1); } fprintf(pidfp, "%d\n", (int) getpid()); fclose(pidfp); } if (srv.port) { LOG(SPOCP_INFO) traceLog( LOG_INFO, "Asked to listen on port %d", srv.port); if ((srv.listen_fd = spocp_stream_socket(srv.port)) < 0) exit(1); srv.id = (char *) Malloc(16); sprintf(srv.id, "spocp-%d", srv.port); srv.type = AF_INET; } else { LOG(SPOCP_INFO) traceLog(LOG_INFO,"Asked to listen on unix domain socket"); if ((srv.listen_fd = spocp_unix_domain_socket(srv.uds)) < 0) exit(1); srv.id = (char *) Malloc(7 + strlen(srv.uds)); /* Flawfinder: ignore */ sprintf(srv.id, "spocp-%s", srv.uds); srv.type = AF_UNIX; } xsignal(SIGCHLD, sig_chld); xsignal(SIGPIPE, sig_pipe); xsignal(SIGINT, sig_int); xsignal(SIGTERM, sig_term); xsignal(SIGUSR1, sig_usr1); clilen = sizeof(cliaddr); DEBUG(SPOCP_DSRV) traceLog(LOG_DEBUG,"Creating threads"); /* * returns the pool the threads are picking work from */ srv.work = tpool_init(srv.threads, 64, 1); spocp_srv_run(&srv); } else { conn_t *conn; saci_init(); DEBUG(SPOCP_DSRV) traceLog(LOG_DEBUG,"---->"); LOG(SPOCP_INFO) traceLog(LOG_INFO,"Reading STDIN"); /* * If I want to use this I have to do init_server() first * conn = spocp_open_connection( STDIN_FILENO, &srv ) ; */ /* * this is much simpler */ conn = conn_new(); conn_setup(conn, &srv, STDIN_FILENO, "localhost", "127.0.0.1"); LOG(SPOCP_INFO) traceLog(LOG_INFO,"Running server"); spocp_server((void *) conn); gettimeofday(&end, NULL); print_elapsed("query time:", start, end); conn_free( conn ); } srv_free( &srv ); if (cnfg != DEF_CNFG) Free( cnfg ); exit(0); }
static connection_struct *make_connection_smb1(struct smb_request *req, NTTIME now, int snum, struct user_struct *vuser, const char *pdev, NTSTATUS *pstatus) { struct smbXsrv_tcon *tcon; NTSTATUS status; struct connection_struct *conn; status = smb1srv_tcon_create(req->xconn, now, &tcon); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("make_connection_smb1: Couldn't find free tcon %s.\n", nt_errstr(status))); *pstatus = status; return NULL; } conn = conn_new(req->sconn); if (!conn) { TALLOC_FREE(tcon); DEBUG(0,("make_connection_smb1: Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->cnum = tcon->global->tcon_wire_id; conn->tcon = tcon; *pstatus = make_connection_snum(req->xconn, conn, snum, vuser, pdev); if (!NT_STATUS_IS_OK(*pstatus)) { conn_free(conn); TALLOC_FREE(tcon); return NULL; } tcon->global->share_name = lp_servicename(tcon->global, SNUM(conn)); if (tcon->global->share_name == NULL) { conn_free(conn); TALLOC_FREE(tcon); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } tcon->global->session_global_id = vuser->session->global->session_global_id; tcon->compat = talloc_move(tcon, &conn); tcon->status = NT_STATUS_OK; *pstatus = smbXsrv_tcon_update(tcon); if (!NT_STATUS_IS_OK(*pstatus)) { TALLOC_FREE(tcon); return NULL; } return tcon->compat; }
/* * Create a new peer session in assigned state (connect will start automatically) */ static struct stream *peer_session_create(struct peer *peer, struct peer_session *ps) { struct listener *l = LIST_NEXT(&peer->peers->peers_fe->conf.listeners, struct listener *, by_fe); struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */ struct appctx *appctx; struct session *sess; struct stream *s; struct task *t; struct connection *conn; ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000)); ps->statuscode = PEER_SESS_SC_CONNECTCODE; s = NULL; appctx = appctx_new(&peer_applet); if (!appctx) goto out_close; appctx->st0 = PEER_SESS_ST_CONNECT; appctx->ctx.peers.ptr = (void *)ps; sess = session_new(p, l, &appctx->obj_type); if (!sess) { Alert("out of memory in peer_session_create().\n"); goto out_free_appctx; } if ((t = task_new()) == NULL) { Alert("out of memory in peer_session_create().\n"); goto out_free_sess; } t->nice = l->nice; if ((s = stream_new(sess, t, &appctx->obj_type)) == NULL) { Alert("Failed to initialize stream in peer_session_create().\n"); goto out_free_task; } /* The tasks below are normally what is supposed to be done by * fe->accept(). */ s->flags = SF_ASSIGNED|SF_ADDR_SET; /* applet is waiting for data */ si_applet_cant_get(&s->si[0]); appctx_wakeup(appctx); /* initiate an outgoing connection */ si_set_state(&s->si[1], SI_ST_ASS); /* automatically prepare the stream interface to connect to the * pre-initialized connection in si->conn. */ if (unlikely((conn = conn_new()) == NULL)) goto out_free_strm; conn_prepare(conn, peer->proto, peer->xprt); si_attach_conn(&s->si[1], conn); conn->target = s->target = &s->be->obj_type; memcpy(&conn->addr.to, &peer->addr, sizeof(conn->addr.to)); s->do_log = NULL; s->uniq_id = 0; s->res.flags |= CF_READ_DONTWAIT; l->nbconn++; /* warning! right now, it's up to the handler to decrease this */ p->feconn++;/* beconn will be increased later */ jobs++; if (!(s->sess->listener->options & LI_O_UNLIMITED)) actconn++; totalconn++; ps->appctx = appctx; ps->stream = s; return s; /* Error unrolling */ out_free_strm: LIST_DEL(&s->list); pool_free2(pool2_stream, s); out_free_task: task_free(t); out_free_sess: session_free(sess); out_free_appctx: appctx_free(appctx); out_close: return s; }
static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = (LIBEVENT_THREAD *)arg; CQ_ITEM *item; char buf[1]; if (read(fd, buf, 1) != 1) merror("can't read from libevent pipe!"); switch(buf[0]) { case 'c': { item = cq_pop(me->new_conn_queue); if (NULL != item) { listener_info *li = (listener_info *)item->data; conn *c = conn_new(); if (NULL == c) { } else { struct bufferevent* bev = bufferevent_socket_new(me->base, item->fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); if (NULL == bev) { merror("create bufferevent failed!"); } else { strncpy(c->addrtext, li->addrtext, 32); evbuffer *input = bufferevent_get_input(bev); evbuffer_enable_locking(input, NULL); bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, c); bufferevent_enable(bev, EV_READ); c->data = li->l; c->bev = bev; c->thread = me; mdebug("new connection %s established!", c->addrtext); } } free(item->data); cqi_free(item); } } break; case 't': { item = cq_pop(me->new_conn_queue); if (NULL != item) { connector *cr = (connector *)item->data; conn *c = conn_new(); if (NULL == c) { } else { struct bufferevent *bev = bufferevent_socket_new(me->base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); if (NULL == bev) { merror("create bufferevent failed!"); } else { evbuffer *input = bufferevent_get_input(bev); evbuffer_enable_locking(input, NULL); bufferevent_setcb(bev, NULL, NULL, connecting_event_cb, c); c->bev = bev; c->data = cr; c->thread = me; cr->c = c; cr->state = STATE_NOT_CONNECTED; minfo("connecting %s!", cr->addrtext); bufferevent_socket_connect(c->bev, cr->sa, cr->socklen); } } cqi_free(item); } } break; case 'k': { event_base_loopbreak(me->base); } break; } }
static connection_struct *make_connection_snum(int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *status) { struct passwd *pass = NULL; BOOL guest = False; connection_struct *conn; SMB_STRUCT_STAT st; fstring user; fstring dev; int ret; struct timespec atime_ts, mtime_ts, ctime_ts; *user = 0; fstrcpy(dev, pdev); SET_STAT_INVALID(st); if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) { return NULL; } conn = conn_new(); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *status = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->params->service = snum; conn->nt_user_token = NULL; if (lp_guest_only(snum)) { const char *guestname = lp_guestaccount(); NTSTATUS status2; char *found_username = NULL; guest = True; pass = getpwnam_alloc(NULL, guestname); if (!pass) { DEBUG(0,("make_connection_snum: Invalid guest " "account %s??\n",guestname)); conn_free(conn); *status = NT_STATUS_NO_SUCH_USER; return NULL; } status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, &conn->uid, &conn->gid, &found_username, &conn->nt_user_token); if (!NT_STATUS_IS_OK(status2)) { TALLOC_FREE(pass); conn_free(conn); *status = status2; return NULL; } fstrcpy(user, found_username); string_set(&conn->user,user); conn->force_user = True; TALLOC_FREE(found_username); TALLOC_FREE(pass); DEBUG(3,("Guest only user %s\n",user)); } else if (vuser) { if (vuser->guest) { if (!lp_guest_ok(snum)) { DEBUG(2, ("guest user (from session setup) " "not permitted to access this share " "(%s)\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } } else { if (!user_ok_token(vuser->user.unix_name, vuser->nt_user_token, snum)) { DEBUG(2, ("user '%s' (from session setup) not " "permitted to access this share " "(%s)\n", vuser->user.unix_name, lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } } conn->vuid = vuser->vuid; conn->uid = vuser->uid; conn->gid = vuser->gid; string_set(&conn->user,vuser->user.unix_name); fstrcpy(user,vuser->user.unix_name); guest = vuser->guest; } else if (lp_security() == SEC_SHARE) { NTSTATUS status2; char *found_username = NULL; /* add it as a possible user name if we are in share mode security */ add_session_user(lp_servicename(snum)); /* shall we let them in? */ if (!authorise_login(snum,user,password,&guest)) { DEBUG( 2, ( "Invalid username/password for [%s]\n", lp_servicename(snum)) ); conn_free(conn); *status = NT_STATUS_WRONG_PASSWORD; return NULL; } pass = Get_Pwnam(user); status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, &conn->uid, &conn->gid, &found_username, &conn->nt_user_token); if (!NT_STATUS_IS_OK(status2)) { conn_free(conn); *status = status2; return NULL; } fstrcpy(user, found_username); string_set(&conn->user,user); TALLOC_FREE(found_username); conn->force_user = True; } else { DEBUG(0, ("invalid VUID (vuser) but not in security=share\n")); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } add_session_user(user); safe_strcpy(conn->client_address, client_addr(), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); conn->dirptr = NULL; /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { conn->case_sensitive = (BOOL)lp_casesensitive(snum); } conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; conn->aio_write_behind_list = NULL; string_set(&conn->dirpath,""); string_set(&conn->user,user); conn->read_only = lp_readonly(SNUM(conn)); conn->admin_user = False; /* * If force user is true, then store the given userid and the gid of * the user we're forcing. * For auxiliary groups see below. */ if (*lp_force_user(snum)) { NTSTATUS status2; status2 = find_forced_user(conn, (vuser != NULL) && vuser->guest, user); if (!NT_STATUS_IS_OK(status2)) { conn_free(conn); *status = status2; return NULL; } string_set(&conn->user,user); conn->force_user = True; DEBUG(3,("Forced user %s\n",user)); } /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { NTSTATUS status2; DOM_SID group_sid; status2 = find_forced_group(conn->force_user, snum, user, &group_sid, &conn->gid); if (!NT_STATUS_IS_OK(status2)) { conn_free(conn); *status = status2; return NULL; } if ((conn->nt_user_token == NULL) && (vuser != NULL)) { /* Not force user and not security=share, but force * group. vuser has a token to copy */ conn->nt_user_token = dup_nt_token( NULL, vuser->nt_user_token); if (conn->nt_user_token == NULL) { DEBUG(0, ("dup_nt_token failed\n")); conn_free(conn); *status = NT_STATUS_NO_MEMORY; return NULL; } } /* If conn->nt_user_token is still NULL, we have * security=share. This means ignore the SID, as we had no * vuser to copy from */ if (conn->nt_user_token != NULL) { /* Overwrite the primary group sid */ sid_copy(&conn->nt_user_token->user_sids[1], &group_sid); } conn->force_group = True; } if (conn->nt_user_token != NULL) { size_t i; /* We have a share-specific token from force [user|group]. * This means we have to create the list of unix groups from * the list of sids. */ conn->ngroups = 0; conn->groups = NULL; for (i=0; i<conn->nt_user_token->num_sids; i++) { gid_t gid; DOM_SID *sid = &conn->nt_user_token->user_sids[i]; if (!sid_to_gid(sid, &gid)) { DEBUG(10, ("Could not convert SID %s to gid, " "ignoring it\n", sid_string_static(sid))); continue; } if (!add_gid_to_array_unique(conn->mem_ctx, gid, &conn->groups, &conn->ngroups)) { DEBUG(0, ("add_gid_to_array_unique failed\n")); conn_free(conn); *status = NT_STATUS_NO_MEMORY; return NULL; } } } { pstring s; pstrcpy(s,lp_pathname(snum)); standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, s, sizeof(s)); if (s[0] == '\0') { DEBUG(6, ("service [%s] did not resolve to a path\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } set_conn_connectpath(conn,s); DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ { BOOL can_write = False; NT_USER_TOKEN *token = conn->nt_user_token ? conn->nt_user_token : (vuser ? vuser->nt_user_token : NULL); /* * I don't believe this can happen. But the * logic above is convoluted enough to confuse * automated checkers, so be sure. JRA. */ if (token == NULL) { DEBUG(0,("make_connection: connection to %s " "denied due to missing " "NT token.\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } can_write = share_access_check(token, lp_servicename(snum), FILE_WRITE_DATA); if (!can_write) { if (!share_access_check(token, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; } } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { pstring s; pstrcpy(s,conn->connectpath); canonicalize_path(conn, s); set_conn_connectpath(conn,s); } if ((!conn->printer) && (!conn->ipc)) { conn->notify_ctx = notify_init(conn->mem_ctx, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ /* check number of connections */ if (!claim_connection(conn, lp_servicename(snum), lp_max_connections(snum), False,0)) { DEBUG(1,("too many connections - rejected\n")); conn_free(conn); *status = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { pstring cmd; pstrcpy(cmd,lp_rootpreexec(snum)); standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, cmd, sizeof(cmd)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); if (ret != 0 && lp_rootpreexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_LOGON_FAILURE; return NULL; } /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { pstring cmd; pstrcpy(cmd,lp_preexec(snum)); standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, cmd, sizeof(cmd)); ret = smbrun(cmd,NULL); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); change_to_root_user(); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(snum)); set_namearray( &conn->hide_list, lp_hide_files(snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(snum)); } /* Invoke VFS make connection hook - do this before the VFS_STAT call to allow any filesystems needing user credentials to initialize themselves. */ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); change_to_root_user(); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_UNSUCCESSFUL; return NULL; } /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission check during individual operations. To match this behaviour I have disabled this chdir check (tridge) */ /* the alternative is just to check the directory exists */ if ((ret = SMB_VFS_STAT(conn, conn->connectpath, &st)) != 0 || !S_ISDIR(st.st_mode)) { if (ret == 0 && !S_ISDIR(st.st_mode)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } change_to_root_user(); /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } string_set(&conn->origpath,conn->connectpath); mtime_ts = get_mtimespec(&st); ctime_ts = get_ctimespec(&st); atime_ts = get_atimespec(&st); conn->ts_res = TIMESTAMP_SET_SECONDS; if (mtime_ts.tv_nsec || atime_ts.tv_nsec || ctime_ts.tv_nsec) { /* If any of the normal UNIX directory timestamps * have a non-zero tv_nsec component assume * we might be able to set sub-second timestamps. * See what filetime set primitives we have. */ #if defined(HAVE_UTIMES) /* utimes allows msec timestamps to be set. */ conn->ts_res = TIMESTAMP_SET_MSEC; #elif defined(HAVE_UTIME) /* utime only allows sec timestamps to be set. */ conn->ts_res = TIMESTAMP_SET_SECONDS; #endif /* TODO. Add a configure test for the Linux * nsec timestamp set system call, and use it * if available.... */ DEBUG(10,("make_connection_snum: timestamp " "resolution of %s " "available on share %s, directory %s\n", conn->ts_res == TIMESTAMP_SET_MSEC ? "msec" : "sec", lp_servicename(conn->cnum), conn->connectpath )); } #if SOFTLINK_OPTIMISATION /* resolve any soft links early if possible */ if (vfs_ChDir(conn,conn->connectpath) == 0) { pstring s; pstrcpy(s,conn->connectpath); vfs_GetWd(conn,s); set_conn_connectpath(conn,s); vfs_ChDir(conn,conn->connectpath); } #endif if (lp_unix_extensions() && lp_widelinks(snum)) { DEBUG(0,("Share '%s' has wide links and unix extensions enabled. " "These parameters are incompatible. " "Disabling wide links for this share.\n", lp_servicename(snum) )); lp_do_parameter(snum, "wide links", "False"); } /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); dbgtext( "%s", srv_is_signing_active() ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); dbgtext( "initially as user %s ", user ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)sys_getpid() ); } /* we've finished with the user stuff - go back to root */ change_to_root_user(); return(conn); }
/**************************************************************************** make a connection to a service ****************************************************************************/ connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode) { int snum; struct passwd *pass = NULL; BOOL guest = False; BOOL force = False; extern int Client; connection_struct *conn; int ret; strlower(service); snum = find_service(service); if (snum < 0) { extern int Client; if (strequal(service,"IPC$")) { DEBUG(3,("refusing IPC connection\n")); *ecode = ERRnoipc; return NULL; } DEBUG(0,("%s (%s) couldn't find service %s\n", remote_machine, client_addr(Client), service)); *ecode = ERRinvnetname; return NULL; } if (strequal(service,HOMES_NAME)) { if (*user && Get_Pwnam(user,True)) { fstring dos_username; fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password, pwlen,dev,vuid,ecode)); } if(lp_security() != SEC_SHARE) { if (validated_username(vuid)) { fstring dos_username; fstrcpy(user,validated_username(vuid)); fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } else { /* Security = share. Try with sesssetup_user * as the username. */ if(*sesssetup_user) { fstring dos_username; fstrcpy(user,sesssetup_user); fstrcpy(dos_username, user); unix_to_dos(dos_username, True); return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } } if (!lp_snum_ok(snum) || !check_access(Client, lp_hostsallow(snum), lp_hostsdeny(snum))) { *ecode = ERRaccess; return NULL; } /* you can only connect to the IPC$ service as an ipc device */ if (strequal(service,"IPC$")) pstrcpy(dev,"IPC"); if (*dev == '?' || !*dev) { if (lp_print_ok(snum)) { pstrcpy(dev,"LPT1:"); } else { pstrcpy(dev,"A:"); } } /* if the request is as a printer and you can't print then refuse */ strupper(dev); if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { DEBUG(1,("Attempt to connect to non-printer as a printer\n")); *ecode = ERRinvdevice; return NULL; } /* lowercase the user name */ strlower(user); /* add it as a possible user name */ add_session_user(service); /* shall we let them in? */ if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) { DEBUG( 2, ( "Invalid username/password for %s\n", service ) ); *ecode = ERRbadpw; return NULL; } conn = conn_new(); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *ecode = ERRnoresource; conn_free(conn); return NULL; } /* find out some info about the user */ pass = Get_Pwnam(user,True); if (pass == NULL) { DEBUG(0,( "Couldn't find account %s\n",user)); *ecode = ERRbaduid; conn_free(conn); return NULL; } conn->read_only = lp_readonly(snum); { pstring list; StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = True; StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = False; } /* admin user check */ /* JRA - original code denied admin user if the share was marked read_only. Changed as I don't think this is needed, but old code left in case there is a problem here. */ if (user_in_list(user,lp_admin_users(snum)) #if 0 && !conn->read_only #endif ) { conn->admin_user = True; DEBUG(0,("%s logged in as admin user (root privileges)\n",user)); } else { conn->admin_user = False; } conn->force_user = force; conn->vuid = vuid; conn->uid = pass->pw_uid; conn->gid = pass->pw_gid; safe_strcpy(conn->client_address, client_addr(Client), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = time(NULL); conn->service = snum; conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = (strncmp(dev,"IPC",3) == 0); conn->dirptr = NULL; conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; string_set(&conn->dirpath,""); string_set(&conn->user,user); /* * If force user is true, then store the * given userid and also the primary groupid * of the user we're forcing. */ if (*lp_force_user(snum)) { struct passwd *pass2; pstring fuser; pstrcpy(fuser,lp_force_user(snum)); /* Allow %S to be used by force user. */ pstring_sub(fuser,"%S",service); pass2 = (struct passwd *)Get_Pwnam(fuser,True); if (pass2) { conn->uid = pass2->pw_uid; conn->gid = pass2->pw_gid; string_set(&conn->user,fuser); fstrcpy(user,fuser); conn->force_user = True; DEBUG(3,("Forced user %s\n",fuser)); } else { DEBUG(1,("Couldn't find user %s\n",fuser)); } } #ifdef HAVE_GETGRNAM /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { struct group *gptr; pstring gname; pstring tmp_gname; BOOL user_must_be_member = False; StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1); if (tmp_gname[0] == '+') { user_must_be_member = True; StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2); } else { StrnCpy(gname,tmp_gname,sizeof(pstring)-1); } /* default service may be a group name */ pstring_sub(gname,"%S",service); gptr = (struct group *)getgrnam(gname); if (gptr) { /* * If the user has been forced and the forced group starts * with a '+', then we only set the group to be the forced * group if the forced user is a member of that group. * Otherwise, the meaning of the '+' would be ignored. */ if (conn->force_user && user_must_be_member) { int i; for (i = 0; gptr->gr_mem[i] != NULL; i++) { if (strcmp(user,gptr->gr_mem[i]) == 0) { conn->gid = gptr->gr_gid; DEBUG(3,("Forced group %s for member %s\n",gname,user)); break; } } } else { conn->gid = gptr->gr_gid; DEBUG(3,("Forced group %s\n",gname)); } } else { DEBUG(1,("Couldn't find group %s\n",gname)); } } #endif /* HAVE_GETGRNAM */ { pstring s; pstrcpy(s,lp_pathname(snum)); standard_sub(conn,s); string_set(&conn->connectpath,s); DEBUG(3,("Connect path is %s\n",s)); } /* groups stuff added by ih */ conn->ngroups = 0; conn->groups = NULL; if (!IS_IPC(conn)) { /* Find all the groups this uid is in and store them. Used by become_user() */ setup_groups(conn->user,conn->uid,conn->gid, &conn->ngroups,&conn->groups); /* check number of connections */ if (!claim_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn)), False)) { DEBUG(1,("too many connections - rejected\n")); *ecode = ERRnoresource; conn_free(conn); return NULL; } if (lp_status(SNUM(conn))) claim_connection(conn,"STATUS.", MAXSTATUS,False); } /* IS_IPC */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(SNUM(conn))) { pstring cmd; pstrcpy(cmd,lp_rootpreexec(SNUM(conn))); standard_sub(conn,cmd); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL,False); if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); conn_free(conn); *ecode = ERRsrverror; return NULL; } } if (!become_user(conn, conn->vuid)) { DEBUG(0,("Can't become connected user!\n")); if (!IS_IPC(conn)) { yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); if (lp_status(SNUM(conn))) { yield_connection(conn,"STATUS.",MAXSTATUS); } } conn_free(conn); *ecode = ERRbadpw; return NULL; } if (dos_ChDir(conn->connectpath) != 0) { DEBUG(0,("Can't change directory to %s (%s)\n", conn->connectpath,strerror(errno))); unbecome_user(); if (!IS_IPC(conn)) { yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); if (lp_status(SNUM(conn))) yield_connection(conn,"STATUS.",MAXSTATUS); } conn_free(conn); *ecode = ERRinvnetname; return NULL; } string_set(&conn->origpath,conn->connectpath); #if SOFTLINK_OPTIMISATION /* resolve any soft links early */ { pstring s; pstrcpy(s,conn->connectpath); dos_GetWd(s); string_set(&conn->connectpath,s); dos_ChDir(conn->connectpath); } #endif add_session_user(user); /* execute any "preexec = " line */ if (*lp_preexec(SNUM(conn))) { pstring cmd; pstrcpy(cmd,lp_preexec(SNUM(conn))); standard_sub(conn,cmd); ret = smbrun(cmd,NULL,False); if (ret != 0 && lp_preexec_close(SNUM(conn))) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); conn_free(conn); *ecode = ERRsrverror; return NULL; } } /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using. */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", remote_machine, conn->client_address ); dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); dbgtext( "as user %s ", user ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)getpid() ); } /* we've finished with the sensitive stuff */ unbecome_user(); /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(SNUM(conn))); set_namearray( &conn->hide_list, lp_hide_files(SNUM(conn))); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn))); } return(conn); }
static void mainloop(void) { int i, listenfd, acceptfd; unsigned connCount = 0, idleConnCount, busyConnCount, maxConnCount; struct sockaddr_in addr; ServerConnection **connections, **busyConnections; DataReadySelector *drs; bool isConnMaxWarnPrinted = false; maxConnCount = config_getMaxClients(); if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) log_fatal("socket"); i = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(config_getListenPort()); if( bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)) < 0 ) log_fatal("bind"); if( listen(listenfd, maxConnCount) < 0 ) log_fatal("listen"); if( ! config_switchToTargetUser() ) exit(1); connections = malloc(maxConnCount * sizeof(ServerConnection*)); busyConnections = malloc(maxConnCount * sizeof(ServerConnection*)); drs_setNonBlockingCloExecFlags(listenfd); drs = drs_new(); while( 1 ) { if( connCount < maxConnCount ) drs_setReadFd(drs, listenfd); else if( ! isConnMaxWarnPrinted ) { log_warn("number of clients reached maximum (%u)", maxConnCount); isConnMaxWarnPrinted = true; } drs_select(drs); while( connCount < maxConnCount && (acceptfd = accept(listenfd, NULL, NULL)) >= 0 ) { i = 1; setsockopt(acceptfd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(i)); drs_setNonBlockingCloExecFlags(acceptfd); connections[connCount++] = conn_new(acceptfd); } if( connCount > maxConnCount && errno != EWOULDBLOCK ) log_fatal("accept"); /* keep connections ordered descending by idle time; * close most idle connection when connection number reached maximum */ i = 0; idleConnCount = busyConnCount = 0; while( i < connCount ) { switch( conn_processDataReady(connections[i], drs, connCount == maxConnCount && i == busyConnCount) ) { case CONN_BUSY: busyConnections[busyConnCount++] = connections[i++]; break; case CONN_IDLE: if( i != idleConnCount ) connections[idleConnCount] = connections[i]; ++idleConnCount; ++i; break; default: /* CONN_TO_CLOSE */ conn_free(connections[i]); ++i; break; } } for(i = 0; i < busyConnCount; ++i) connections[idleConnCount+i] = busyConnections[i]; connCount = idleConnCount + busyConnCount; } }
/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = arg; assert(me->type == GENERAL); CQ_ITEM *item; if (recv(fd, devnull, sizeof(devnull), 0) == -1) { if (settings.verbose > 0) { settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL, "Can't read from libevent pipe: %s\n", strerror(errno)); } } if (memcached_shutdown) { event_base_loopbreak(me->base); return ; } while ((item = cq_pop(me->new_conn_queue)) != NULL) { conn *c = conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base, NULL); if (c == NULL) { if (IS_UDP(item->transport)) { settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL, "Can't listen for events on UDP socket\n"); exit(1); } else { if (settings.verbose > 0) { settings.extensions.logger->log(EXTENSION_LOG_INFO, NULL, "Can't listen for events on fd %d\n", item->sfd); } closesocket(item->sfd); } } else { assert(c->thread == NULL); c->thread = me; } cqi_free(item); } pthread_mutex_lock(&me->mutex); conn* pending = me->pending_io; me->pending_io = NULL; pthread_mutex_unlock(&me->mutex); while (pending != NULL) { conn *c = pending; assert(me == c->thread); pending = pending->next; c->next = NULL; register_event(c, 0); /* * We don't want the thread to keep on serving all of the data * from the context of the notification pipe, so just let it * run one time to set up the correct mask in libevent */ c->nevents = 1; /* c->nevents = settings.reqs_per_event; */ while (c->state(c)) { /* do task */ } } }
static NTSTATUS create_conn_struct_as_root(TALLOC_CTX *ctx, struct tevent_context *ev, struct messaging_context *msg, connection_struct **pconn, int snum, const char *path, const struct auth_session_info *session_info) { connection_struct *conn; char *connpath; const char *vfs_user; struct smbd_server_connection *sconn; const char *servicename = lp_const_servicename(snum); sconn = talloc_zero(ctx, struct smbd_server_connection); if (sconn == NULL) { return NT_STATUS_NO_MEMORY; } sconn->ev_ctx = ev; sconn->msg_ctx = msg; sconn->sock = -1; sconn->smb1.echo_handler.trusted_fd = -1; sconn->smb1.echo_handler.socket_lock_fd = -1; conn = conn_new(sconn); if (conn == NULL) { TALLOC_FREE(sconn); return NT_STATUS_NO_MEMORY; } /* Now we have conn, we need to make sconn a child of conn, * for a proper talloc tree */ talloc_steal(conn, sconn); if (snum == -1 && servicename == NULL) { servicename = "Unknown Service (snum == -1)"; } connpath = talloc_strdup(conn, path); if (!connpath) { TALLOC_FREE(conn); return NT_STATUS_NO_MEMORY; } connpath = talloc_string_sub(conn, connpath, "%S", servicename); if (!connpath) { TALLOC_FREE(conn); return NT_STATUS_NO_MEMORY; } /* needed for smbd_vfs_init() */ conn->params->service = snum; conn->cnum = TID_FIELD_INVALID; if (session_info != NULL) { conn->session_info = copy_session_info(conn, session_info); if (conn->session_info == NULL) { DEBUG(0, ("copy_serverinfo failed\n")); TALLOC_FREE(conn); return NT_STATUS_NO_MEMORY; } vfs_user = conn->session_info->unix_info->unix_name; } else { /* use current authenticated user in absence of session_info */ vfs_user = get_current_username(); } set_conn_connectpath(conn, connpath); /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ if (conn->session_info) { share_access_check(conn->session_info->security_token, servicename, MAXIMUM_ALLOWED_ACCESS, &conn->share_access); if ((conn->share_access & FILE_WRITE_DATA) == 0) { if ((conn->share_access & FILE_READ_DATA) == 0) { /* No access, read or write. */ DEBUG(0,("create_conn_struct: connection to %s " "denied due to security " "descriptor.\n", servicename)); conn_free(conn); return NT_STATUS_ACCESS_DENIED; } else { conn->read_only = true; } } } else { conn->share_access = 0; conn->read_only = true; } if (!smbd_vfs_init(conn)) { NTSTATUS status = map_nt_error_from_unix(errno); DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n")); conn_free(conn); return status; } /* this must be the first filesystem operation that we do */ if (SMB_VFS_CONNECT(conn, servicename, vfs_user) < 0) { DEBUG(0,("VFS connect failed!\n")); conn_free(conn); return NT_STATUS_UNSUCCESSFUL; } conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res); *pconn = conn; return NT_STATUS_OK; }
/* This function is called from the protocol layer accept() in order to * instanciate a new session on behalf of a given listener and frontend. It * returns a positive value upon success, 0 if the connection can be ignored, * or a negative value upon critical failure. The accepted file descriptor is * closed if we return <= 0. If no handshake is needed, it immediately tries * to instanciate a new stream. The created connection's owner points to the * new session until the upper layers are created. */ int session_accept_fd(struct listener *l, int cfd, struct sockaddr_storage *addr) { struct connection *cli_conn; struct proxy *p = l->bind_conf->frontend; struct session *sess; int ret; ret = -1; /* assume unrecoverable error by default */ if (unlikely((cli_conn = conn_new()) == NULL)) goto out_close; conn_prepare(cli_conn, l->proto, l->bind_conf->xprt); cli_conn->handle.fd = cfd; cli_conn->addr.from = *addr; cli_conn->flags |= CO_FL_ADDR_FROM_SET; cli_conn->target = &l->obj_type; cli_conn->proxy_netns = l->netns; conn_ctrl_init(cli_conn); /* wait for a PROXY protocol header */ if (l->options & LI_O_ACC_PROXY) { cli_conn->flags |= CO_FL_ACCEPT_PROXY; conn_sock_want_recv(cli_conn); } /* wait for a NetScaler client IP insertion protocol header */ if (l->options & LI_O_ACC_CIP) { cli_conn->flags |= CO_FL_ACCEPT_CIP; conn_sock_want_recv(cli_conn); } conn_xprt_want_recv(cli_conn); if (conn_xprt_init(cli_conn) < 0) goto out_free_conn; sess = session_new(p, l, &cli_conn->obj_type); if (!sess) goto out_free_conn; conn_set_owner(cli_conn, sess, NULL); /* now evaluate the tcp-request layer4 rules. We only need a session * and no stream for these rules. */ if ((l->options & LI_O_TCP_L4_RULES) && !tcp_exec_l4_rules(sess)) { /* let's do a no-linger now to close with a single RST. */ setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); ret = 0; /* successful termination */ goto out_free_sess; } /* monitor-net and health mode are processed immediately after TCP * connection rules. This way it's possible to block them, but they * never use the lower data layers, they send directly over the socket, * as they were designed for. We first flush the socket receive buffer * in order to avoid emission of an RST by the system. We ignore any * error. */ if (unlikely((p->mode == PR_MODE_HEALTH) || ((l->options & LI_O_CHK_MONNET) && addr->ss_family == AF_INET && (((struct sockaddr_in *)addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr))) { /* we have 4 possibilities here : * - HTTP mode, from monitoring address => send "HTTP/1.0 200 OK" * - HEALTH mode with HTTP check => send "HTTP/1.0 200 OK" * - HEALTH mode without HTTP check => just send "OK" * - TCP mode from monitoring address => just close */ if (l->proto->drain) l->proto->drain(cfd); if (p->mode == PR_MODE_HTTP || (p->mode == PR_MODE_HEALTH && (p->options2 & PR_O2_CHK_ANY) == PR_O2_HTTP_CHK)) send(cfd, "HTTP/1.0 200 OK\r\n\r\n", 19, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); else if (p->mode == PR_MODE_HEALTH) send(cfd, "OK\n", 3, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_MORE); ret = 0; goto out_free_sess; } /* Adjust some socket options */ if (l->addr.ss_family == AF_INET || l->addr.ss_family == AF_INET6) { setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_CLI_KA) setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); if (p->options & PR_O_TCP_NOLING) fdtab[cfd].linger_risk = 1; #if defined(TCP_MAXSEG) if (l->maxseg < 0) { /* we just want to reduce the current MSS by that value */ int mss; socklen_t mss_len = sizeof(mss); if (getsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &mss_len) == 0) { mss += l->maxseg; /* remember, it's < 0 */ setsockopt(cfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)); } } #endif } if (global.tune.client_sndbuf) setsockopt(cfd, SOL_SOCKET, SO_SNDBUF, &global.tune.client_sndbuf, sizeof(global.tune.client_sndbuf)); if (global.tune.client_rcvbuf) setsockopt(cfd, SOL_SOCKET, SO_RCVBUF, &global.tune.client_rcvbuf, sizeof(global.tune.client_rcvbuf)); /* OK, now either we have a pending handshake to execute with and then * we must return to the I/O layer, or we can proceed with the end of * the stream initialization. In case of handshake, we also set the I/O * timeout to the frontend's client timeout and register a task in the * session for this purpose. The connection's owner is left to the * session during this period. * * At this point we set the relation between sess/task/conn this way : * * +----------------- task * | | * orig -- sess <-- context | * | ^ | | * v | | | * conn -- owner ---> task <-----+ */ if (cli_conn->flags & (CO_FL_HANDSHAKE | CO_FL_EARLY_SSL_HS)) { if (unlikely((sess->task = task_new(tid_bit)) == NULL)) goto out_free_sess; conn_set_xprt_done_cb(cli_conn, conn_complete_session); sess->task->context = sess; sess->task->nice = l->nice; sess->task->process = session_expire_embryonic; sess->task->expire = tick_add_ifset(now_ms, p->timeout.client); task_queue(sess->task); return 1; } /* OK let's complete stream initialization since there is no handshake */ if (conn_complete_session(cli_conn) >= 0) return 1; /* error unrolling */ out_free_sess: /* prevent call to listener_release during session_free. It will be * done below, for all errors. */ sess->listener = NULL; session_free(sess); out_free_conn: conn_stop_tracking(cli_conn); conn_xprt_close(cli_conn); conn_free(cli_conn); out_close: listener_release(l); if (ret < 0 && l->bind_conf->xprt == xprt_get(XPRT_RAW) && p->mode == PR_MODE_HTTP) { /* critical error, no more memory, try to emit a 500 response */ struct chunk *err_msg = &p->errmsg[HTTP_ERR_500]; if (!err_msg->str) err_msg = &http_err_chunks[HTTP_ERR_500]; send(cfd, err_msg->str, err_msg->len, MSG_DONTWAIT|MSG_NOSIGNAL); } if (fdtab[cfd].owner) fd_delete(cfd); else close(cfd); return ret; }
int xmpp_server_child_process(int data_pipe) { int rv; int listen_fd; fd_set fdset; struct xmpp_connection *conn; snprintf(local_secret, sizeof(local_secret), "%s", random_secret()); while ((listen_fd = net_listen(xmpp_domain, xmpp_port)) < 0) { /* ugh. */ sleep(3); } while (1) { FD_ZERO(&fdset); FD_SET(data_pipe, &fdset); FD_SET(listen_fd, &fdset); /* check for dead connections */ for (conn = conn_list; conn; ) { struct xmpp_connection *next = conn->next; if (conn->type == CONN_DEAD) conn_free(conn); conn = next; } for (conn = conn_list; conn; conn = conn->next) { /* check if we need to set up a connection */ if (conn->type == CONN_OUTBOUND && conn->fd == -1) { if ((conn->fd = net_connect(conn->domain, xmpp_port)) >= 0) { net_printf(conn->fd, "<?xml version='1.0'?>" "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:server' version='1.0' " "xmlns:db='jabber:server:dialback' to='%s' from='%s'>", conn->domain, xmpp_domain); net_printf(conn->fd, "<stream:features xmlns:stream='http://etherx.jabber.org/streams'/>"); } else { conn->type = CONN_DEAD; } } if (conn->fd != -1) FD_SET(conn->fd, &fdset); } rv = select(FD_SETSIZE, &fdset, NULL, NULL, NULL); /* update the local config framework structures */ cfg_update(); if (rv < 0) { LM_ERR("select() failed: %s\n", strerror(errno)); } else if (!rv) { /* timeout */ } else { for (conn = conn_list; conn; conn = conn->next) { if (conn->fd != -1 && FD_ISSET(conn->fd, &fdset)) { char *buf = net_read_static(conn->fd); if (!buf) { conn->type = CONN_DEAD; } else { LM_DBG("stream (fd %d, domain '%s') read\n[%s]\n", conn->fd, conn->domain, buf); xode_stream_eat(conn->stream, buf, strlen(buf)); } } } if (FD_ISSET(listen_fd, &fdset)) { struct sockaddr_in sin; unsigned int len = sizeof(sin); int fd; if ((fd = accept(listen_fd,(struct sockaddr*)&sin, &len))<0) { LM_ERR("accept() failed: %s\n", strerror(errno)); } else { LM_DBG("accept()ed connection from %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); conn_new(CONN_INBOUND, fd, NULL); } } if (FD_ISSET(data_pipe, &fdset)) { struct xmpp_pipe_cmd *cmd; if (read(data_pipe, &cmd, sizeof(cmd)) != sizeof(cmd)) { LM_ERR("failed to read from command pipe: %s\n", strerror(errno)); } else { LM_DBG("got pipe cmd %d\n", cmd->type); switch (cmd->type) { case XMPP_PIPE_SEND_MESSAGE: do_send_message_server(cmd); break; case XMPP_PIPE_SEND_PACKET: case XMPP_PIPE_SEND_PSUBSCRIBE: case XMPP_PIPE_SEND_PNOTIFY: break; } xmpp_free_pipe_cmd(cmd); } } } } return 0; }
connection_struct *make_connection_snum(struct smbd_server_connection *sconn, int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *pstatus) { connection_struct *conn; struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; char addr[INET6_ADDRSTRLEN]; bool on_err_call_dis_hook = false; NTSTATUS status; fstrcpy(dev, pdev); if (NT_STATUS_IS_ERR(*pstatus = share_sanity_checks(snum, dev))) { return NULL; } conn = conn_new(sconn); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->params->service = snum; status = create_connection_server_info(sconn, conn, snum, vuser ? vuser->server_info : NULL, password, &conn->server_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("create_connection_server_info failed: %s\n", nt_errstr(status))); *pstatus = status; conn_free(conn); return NULL; } if ((lp_guest_only(snum)) || (lp_security() == SEC_SHARE)) { conn->force_user = true; } add_session_user(sconn, conn->server_info->unix_name); safe_strcpy(conn->client_address, client_addr(get_client_fd(),addr,sizeof(addr)), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { conn->case_sensitive = (bool)lp_casesensitive(snum); } conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); conn->encrypt_level = lp_smb_encrypt(snum); conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; conn->aio_write_behind_list = NULL; conn->read_only = lp_readonly(SNUM(conn)); conn->admin_user = False; if (*lp_force_user(snum)) { /* * Replace conn->server_info with a completely faked up one * from the username we are forced into :-) */ char *fuser; struct auth_serversupplied_info *forced_serverinfo; fuser = talloc_string_sub(conn, lp_force_user(snum), "%S", lp_servicename(snum)); if (fuser == NULL) { conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } status = make_serverinfo_from_username( conn, fuser, conn->server_info->guest, &forced_serverinfo); if (!NT_STATUS_IS_OK(status)) { conn_free(conn); *pstatus = status; return NULL; } TALLOC_FREE(conn->server_info); conn->server_info = forced_serverinfo; conn->force_user = True; DEBUG(3,("Forced user %s\n", fuser)); } /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { status = find_forced_group( conn->force_user, snum, conn->server_info->unix_name, &conn->server_info->ptok->user_sids[1], &conn->server_info->utok.gid); if (!NT_STATUS_IS_OK(status)) { conn_free(conn); *pstatus = status; return NULL; } /* * We need to cache this gid, to use within * change_to_user() separately from the conn->server_info * struct. We only use conn->server_info directly if * "force_user" was set. */ conn->force_group_gid = conn->server_info->utok.gid; } conn->vuid = (vuser != NULL) ? vuser->vuid : UID_FIELD_INVALID; { char *s = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_pathname(snum)); if (!s) { conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } if (!set_conn_connectpath(conn,s)) { TALLOC_FREE(s); conn_free(conn); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); TALLOC_FREE(s); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ { bool can_write = False; can_write = share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_WRITE_DATA); if (!can_write) { if (!share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; } } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { if (!canonicalize_connect_path(conn)) { DEBUG(0, ("canonicalize_connect_path failed " "for service %s, path %s\n", lp_servicename(snum), conn->connectpath)); conn_free(conn); *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } } if ((!conn->printer) && (!conn->ipc)) { conn->notify_ctx = notify_init(conn, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ /* * Enforce the max connections parameter. */ if ((lp_max_connections(snum) > 0) && (count_current_connections(lp_servicename(SNUM(conn)), True) >= lp_max_connections(snum))) { DEBUG(1, ("Max connections (%d) exceeded for %s\n", lp_max_connections(snum), lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } /* * Get us an entry in the connections db */ if (!claim_connection(conn, lp_servicename(snum), 0)) { DEBUG(1, ("Could not store connections entry\n")); conn_free(conn); *pstatus = NT_STATUS_INTERNAL_DB_ERROR; return NULL; } /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_rootpreexec(snum)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_rootpreexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *pstatus = NT_STATUS_LOGON_FAILURE; return NULL; } /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->server_info->unix_name, conn->connectpath, conn->server_info->utok.gid, conn->server_info->sanitized_username, pdb_get_domain(conn->server_info->sam_account), lp_preexec(snum)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(snum)); set_namearray( &conn->hide_list, lp_hide_files(snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(snum)); set_namearray( &conn->aio_write_behind_list, lp_aio_write_behind(snum)); } /* Invoke VFS make connection hook - do this before the VFS_STAT call to allow any filesystems needing user credentials to initialize themselves. */ if (SMB_VFS_CONNECT(conn, lp_servicename(snum), conn->server_info->unix_name) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); *pstatus = NT_STATUS_UNSUCCESSFUL; goto err_root_exit; } /* Any error exit after here needs to call the disconnect hook. */ on_err_call_dis_hook = true; status = create_synthetic_smb_fname(talloc_tos(), conn->connectpath, NULL, NULL, &smb_fname_cpath); if (!NT_STATUS_IS_OK(status)) { *pstatus = status; goto err_root_exit; } /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission check during individual operations. To match this behaviour I have disabled this chdir check (tridge) */ /* the alternative is just to check the directory exists */ if ((ret = SMB_VFS_STAT(conn, smb_fname_cpath)) != 0 || !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { if (ret == 0 && !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } *pstatus = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } string_set(&conn->origpath,conn->connectpath); #if SOFTLINK_OPTIMISATION /* resolve any soft links early if possible */ if (vfs_ChDir(conn,conn->connectpath) == 0) { TALLOC_CTX *ctx = talloc_tos(); char *s = vfs_GetWd(ctx,s); if (!s) { *status = map_nt_error_from_unix(errno); goto err_root_exit; } if (!set_conn_connectpath(conn,s)) { *status = NT_STATUS_NO_MEMORY; goto err_root_exit; } vfs_ChDir(conn,conn->connectpath); } #endif if (lp_unix_extensions() && lp_widelinks(snum)) { DEBUG(0,("Share '%s' has wide links and unix extensions enabled. " "These parameters are incompatible. " "Disabling wide links for this share.\n", lp_servicename(snum) )); lp_do_parameter(snum, "wide links", "False"); } /* Figure out the characteristics of the underlying filesystem. This * assumes that all the filesystem mounted withing a share path have * the same characteristics, which is likely but not guaranteed. */ conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res); /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); dbgtext( "%s", srv_is_signing_active(smbd_server_conn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); dbgtext( "initially as user %s ", conn->server_info->unix_name ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)sys_getpid() ); } /* we've finished with the user stuff - go back to root */ change_to_root_user(); return(conn); err_root_exit: TALLOC_FREE(smb_fname_cpath); change_to_root_user(); if (on_err_call_dis_hook) { /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); } yield_connection(conn, lp_servicename(snum)); conn_free(conn); return NULL; }
/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = arg; assert(me->type == GENERAL); CQ_ITEM *item; if (recv(fd, devnull, sizeof(devnull), 0) == -1) { if (settings.verbose > 0) { settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL, "Can't read from libevent pipe: %s\n", strerror(errno)); } } if (memcached_shutdown) { event_base_loopbreak(me->base); return ; } while ((item = cq_pop(me->new_conn_queue)) != NULL) { conn *c = conn_new(item->sfd, item->parent_port, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base, NULL); if (c == NULL) { if (IS_UDP(item->transport)) { settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL, "Can't listen for events on UDP socket\n"); exit(1); } else { if (settings.verbose > 0) { settings.extensions.logger->log(EXTENSION_LOG_INFO, NULL, "Can't listen for events on fd %d\n", item->sfd); } closesocket(item->sfd); } } else { assert(c->thread == NULL); c->thread = me; } cqi_free(item); } LOCK_THREAD(me); conn* pending = me->pending_io; me->pending_io = NULL; while (pending != NULL) { conn *c = pending; assert(me == c->thread); pending = pending->next; c->next = NULL; if (c->sfd != INVALID_SOCKET && !c->registered_in_libevent) { // The socket may have been shut down while we're looping // in delayed shutdown register_event(c, 0); } /* * We don't want the thread to keep on serving all of the data * from the context of the notification pipe, so just let it * run one time to set up the correct mask in libevent */ c->nevents = 1; do { if (settings.verbose) { settings.extensions.logger->log(EXTENSION_LOG_DEBUG, c, "%d - Running task: (%s)\n", c->sfd, state_text(c->state)); } } while (c->state(c)); } UNLOCK_THREAD(me); }
/* * Processes an incoming "handle a new connection" item. This is called when * input arrives on the libevent wakeup pipe. */ static void thread_libevent_process(int fd, short which, void *arg) { LIBEVENT_THREAD *me = arg; CQ_ITEM *item; char buf[1]; conn *c; unsigned int timeout_fd; if (read(fd, buf, 1) != 1) { if (settings.verbose > 0) fprintf(stderr, "Can't read from libevent pipe\n"); return; } switch (buf[0]) { case 'c': item = cq_pop(me->new_conn_queue); if (NULL == item) { break; } switch (item->mode) { case queue_new_conn: c = conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size, item->transport, me->base); if (c == NULL) { if (IS_UDP(item->transport)) { fprintf(stderr, "Can't listen for events on UDP socket\n"); exit(1); } else { if (settings.verbose > 0) { fprintf(stderr, "Can't listen for events on fd %d\n", item->sfd); } close(item->sfd); } } else { c->thread = me; } break; case queue_redispatch: conn_worker_readd(item->c); break; } cqi_free(item); break; /* we were told to pause and report in */ case 'p': register_thread_initialized(); break; /* a client socket timed out */ case 't': if (read(fd, &timeout_fd, sizeof(timeout_fd)) != sizeof(timeout_fd)) { if (settings.verbose > 0) fprintf(stderr, "Can't read timeout fd from libevent pipe\n"); return; } conn_close_idle(conns[timeout_fd]); break; } }
static int server_socket(const char *interface, int port, enum network_transport transport, FILE *portnumber_file){ /* 该函数主要作用: 1.监听端口,注册监听端口事件. 2.一旦有端口有数据,建立连接,将连接放到连接池中,conn_new为每一个连接注册时间监听 3.通知线程有连接需要处理 */ int sfd; struct linger ling = {0, 0}; struct addrinfo *ai; struct addrinfo *next; struct addrinfo hints = { .ai_flags = AI_PASSIVE, .ai_family = AF_UNSPEC }; char port_buf[NI_MAXSERV]; int error; int success = 0; int flags =1; hints.ai_socktype = SOCK_STREAM; if (port == -1) { port = 0; } snprintf(port_buf, sizeof(port_buf), "%d", port); error= getaddrinfo(interface, port_buf, &hints, &ai); if (error != 0) { if (error != EAI_SYSTEM) fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); else perror("getaddrinfo()"); return 1; } for (next= ai; next; next= next->ai_next) { conn *listen_conn_add; if ((sfd = new_socket(next)) == -1) { /* getaddrinfo can return "junk" addresses, * we make sure at least one works before erroring. */ if (errno == EMFILE) { /* ...unless we're out of fds */ perror("server_socket"); exit(EX_OSERR); } continue; } setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); if (error != 0) perror("setsockopt"); error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); if (error != 0) perror("setsockopt"); error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); if (error != 0) perror("setsockopt"); if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) { if (errno != EADDRINUSE) { perror("bind()"); close(sfd); freeaddrinfo(ai); return 1; } close(sfd); continue; } else { success++; if (!IS_UDP(transport) && listen(sfd, 1024) == -1) { perror("listen()"); close(sfd); freeaddrinfo(ai); return 1; } if (portnumber_file != NULL && (next->ai_addr->sa_family == AF_INET || next->ai_addr->sa_family == AF_INET6)) { union { struct sockaddr_in in; struct sockaddr_in6 in6; } my_sockaddr; socklen_t len = sizeof(my_sockaddr); if (getsockname(sfd, (struct sockaddr*)&my_sockaddr, &len)==0) { if (next->ai_addr->sa_family == AF_INET) { fprintf(portnumber_file, "%s INET: %u\n", IS_UDP(transport) ? "UDP" : "TCP", ntohs(my_sockaddr.in.sin_port)); } else { fprintf(portnumber_file, "%s INET6: %u\n", IS_UDP(transport) ? "UDP" : "TCP", ntohs(my_sockaddr.in6.sin6_port)); } } } } if (!(listen_conn_add = conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1, transport, main_base))) { fprintf(stderr, "failed to create listening connection\n"); exit(EXIT_FAILURE); } listen_conn_add->next = listen_conn; listen_conn = listen_conn_add; } freeaddrinfo(ai); /* Return zero iff we detected no errors in starting up connections */ return success == 0; }