void xfer_network_send_file_fork (struct t_xfer *xfer) { pid_t pid; int rc; if (!xfer_network_create_pipe (xfer)) return; xfer->file = open (xfer->local_filename, O_RDONLY | O_NONBLOCK, 0644); switch (pid = fork ()) { case -1: /* fork failed */ dogechat_printf (NULL, _("%s%s: unable to fork"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return; case 0: /* child process */ rc = setuid (getuid ()); (void) rc; close (xfer->child_read); switch (xfer->protocol) { case XFER_NO_PROTOCOL: _exit (EXIT_SUCCESS); break; case XFER_PROTOCOL_DCC: xfer_dcc_send_file_child (xfer); break; case XFER_NUM_PROTOCOLS: break; } _exit (EXIT_SUCCESS); } dogechat_printf (NULL, _("%s: sending file to %s (%s, %s.%s), " "name: %s (local filename: %s), %llu bytes (protocol: %s)"), XFER_PLUGIN_NAME, xfer->remote_nick, xfer->remote_address_str, xfer->plugin_name, xfer->plugin_id, xfer->filename, xfer->local_filename, xfer->size, xfer_protocol_string[xfer->protocol]); /* parent process */ xfer->child_pid = pid; close (xfer->child_write); xfer->child_write = -1; xfer->hook_fd = dogechat_hook_fd (xfer->child_read, 1, 0, 0, &xfer_network_child_read_cb, xfer); }
void xfer_network_recv_file_fork (struct t_xfer *xfer) { pid_t pid; int rc; if (!xfer_network_create_pipe (xfer)) return; if (xfer->start_resume > 0) xfer->file = open (xfer->local_filename, O_APPEND | O_WRONLY | O_NONBLOCK); else xfer->file = open (xfer->local_filename, O_CREAT | O_TRUNC | O_WRONLY | O_NONBLOCK, 0644); switch (pid = fork ()) { case -1: /* fork failed */ dogechat_printf (NULL, _("%s%s: unable to fork"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return; case 0: /* child process */ rc = setuid (getuid ()); (void) rc; close (xfer->child_read); switch (xfer->protocol) { case XFER_NO_PROTOCOL: _exit (EXIT_SUCCESS); break; case XFER_PROTOCOL_DCC: xfer_dcc_recv_file_child (xfer); break; case XFER_NUM_PROTOCOLS: break; } _exit (EXIT_SUCCESS); } /* parent process */ xfer->child_pid = pid; close (xfer->child_write); xfer->child_write = -1; xfer->hook_fd = dogechat_hook_fd (xfer->child_read, 1, 0, 0, &xfer_network_child_read_cb, xfer); }
int relay_server_create_socket (struct t_relay_server *server) { int domain, set, max_clients, addr_size; struct sockaddr_in server_addr; struct sockaddr_in6 server_addr6; const char *bind_address; void *ptr_addr; bind_address = dogechat_config_string (relay_config_network_bind_address); if (server->ipv6) { domain = AF_INET6; memset (&server_addr6, 0, sizeof (struct sockaddr_in6)); server_addr6.sin6_family = domain; server_addr6.sin6_port = htons (server->port); server_addr6.sin6_addr = in6addr_any; if (bind_address && bind_address[0]) { if (!inet_pton (domain, bind_address, &server_addr6.sin6_addr)) { dogechat_printf (NULL, /* TRANSLATORS: second "%s" is "IPv4" or "IPv6" */ _("%s%s: invalid bind address \"%s\" for %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, bind_address, "IPv6"); return 0; } } ptr_addr = &server_addr6; addr_size = sizeof (struct sockaddr_in6); } else { domain = AF_INET; memset (&server_addr, 0, sizeof (struct sockaddr_in)); server_addr.sin_family = domain; server_addr.sin_port = htons (server->port); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind_address && bind_address[0]) { if (!inet_pton (domain, bind_address, &server_addr.sin_addr)) { dogechat_printf (NULL, /* TRANSLATORS: second "%s" is "IPv4" or "IPv6" */ _("%s%s: invalid bind address \"%s\" for %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, bind_address, "IPv4"); return 0; } } ptr_addr = &server_addr; addr_size = sizeof (struct sockaddr_in); } /* create socket */ server->sock = socket (domain, SOCK_STREAM, 0); if (server->sock < 0) { dogechat_printf (NULL, _("%s%s: cannot create socket: error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, errno, strerror (errno)); if (errno == EAFNOSUPPORT) { dogechat_printf (NULL, _("%s%s: try /set relay.network.ipv6 off"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME); } return 0; } #ifdef IPV6_V6ONLY /* set option IPV6_V6ONLY to 0 or 1 */ if (server->ipv6) { set = (server->ipv4) ? 0 : 1; if (setsockopt (server->sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &set, sizeof (set)) < 0) { dogechat_printf (NULL, _("%s%s: cannot set socket option \"%s\" " "to %d: error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, "IPV6_V6ONLY", set, errno, strerror (errno)); close (server->sock); server->sock = -1; return 0; } } #endif /* IPV6_V6ONLY */ /* set option SO_REUSEADDR to 1 */ set = 1; if (setsockopt (server->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &set, sizeof (set)) < 0) { dogechat_printf (NULL, _("%s%s: cannot set socket option \"%s\" to %d: " "error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, "SO_REUSEADDR", set, errno, strerror (errno)); close (server->sock); server->sock = -1; return 0; } /* set option SO_KEEPALIVE to 1 */ set = 1; if (setsockopt (server->sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &set, sizeof (set)) < 0) { dogechat_printf (NULL, _("%s%s: cannot set socket option \"%s\" to %d: " "error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, "SO_KEEPALIVE", set, errno, strerror (errno)); close (server->sock); server->sock = -1; return 0; } /* bind */ if (bind (server->sock, (struct sockaddr *)ptr_addr, addr_size) < 0) { dogechat_printf (NULL, _("%s%s: cannot \"bind\" on port %d (%s): error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, server->port, server->protocol_string, errno, strerror (errno)); close (server->sock); server->sock = -1; return 0; } max_clients = dogechat_config_integer (relay_config_network_max_clients); if (listen (server->sock, max_clients) != 0) { dogechat_printf (NULL, _("%s%s: cannot \"listen\" on port %d (%s): error %d %s"), dogechat_prefix ("error"), RELAY_PLUGIN_NAME, server->port, server->protocol_string, errno, strerror (errno)); close (server->sock); server->sock = -1; return 0; } dogechat_printf (NULL, _("%s: listening on port %d (relay: %s, %s, max %d clients)"), RELAY_PLUGIN_NAME, server->port, server->protocol_string, ((server->ipv4 && server->ipv6) ? "IPv4+6" : ((server->ipv6) ? "IPv6" : "IPv4")), max_clients); server->hook_fd = dogechat_hook_fd (server->sock, 1, 0, 0, &relay_server_sock_cb, server); server->start_time = time (NULL); return 1; }
int fifo_read (void *data, int fd) { static char buffer[4096 + 2]; char *buf2, *pos, *ptr_buf, *next_ptr_buf; int num_read; /* make C compiler happy */ (void) data; (void) fd; num_read = read (fifo_fd, buffer, sizeof (buffer) - 2); if (num_read > 0) { buffer[num_read] = '\0'; buf2 = NULL; ptr_buf = buffer; if (fifo_unterminated) { buf2 = malloc (strlen (fifo_unterminated) + strlen (buffer) + 1); if (buf2) { strcpy (buf2, fifo_unterminated); strcat (buf2, buffer); } ptr_buf = buf2; free (fifo_unterminated); fifo_unterminated = NULL; } while (ptr_buf && ptr_buf[0]) { next_ptr_buf = NULL; pos = strstr (ptr_buf, "\r\n"); if (pos) { pos[0] = '\0'; next_ptr_buf = pos + 2; } else { pos = strstr (ptr_buf, "\n"); if (pos) { pos[0] = '\0'; next_ptr_buf = pos + 1; } else { fifo_unterminated = strdup (ptr_buf); ptr_buf = NULL; next_ptr_buf = NULL; } } if (ptr_buf) fifo_exec (ptr_buf); ptr_buf = next_ptr_buf; } if (buf2) free (buf2); } else { if (num_read < 0) { #ifdef __CYGWIN__ if ((errno == EAGAIN) || (errno == ECOMM)) #else if (errno == EAGAIN) #endif /* __CYGWIN__ */ return DOGECHAT_RC_OK; dogechat_printf (NULL, _("%s%s: error reading pipe (%d %s), closing it"), dogechat_prefix ("error"), FIFO_PLUGIN_NAME, errno, strerror (errno)); fifo_remove (); } else { dogechat_unhook (fifo_fd_hook); fifo_fd_hook = NULL; close (fifo_fd); fifo_fd = open (fifo_filename, O_RDONLY | O_NONBLOCK); if (fifo_fd < 0) { dogechat_printf (NULL, _("%s%s: error opening file, closing it"), dogechat_prefix ("error"), FIFO_PLUGIN_NAME); fifo_remove (); } else fifo_fd_hook = dogechat_hook_fd (fifo_fd, 1, 0, 0, &fifo_read, NULL); } } return DOGECHAT_RC_OK; }
void fifo_create () { int filename_length; const char *fifo_option, *dogechat_home; fifo_option = dogechat_config_get_plugin ("fifo"); if (!fifo_option) { dogechat_config_set_plugin ("fifo", "on"); fifo_option = dogechat_config_get_plugin ("fifo"); } dogechat_home = dogechat_info_get ("dogechat_dir", ""); if (fifo_option && dogechat_home) { fifo_remove_old_pipes (); if (dogechat_strcasecmp (fifo_option, "on") == 0) { /* * build FIFO filename: * "<dogechat_home>/dogechat_fifo_" + process PID */ if (!fifo_filename) { filename_length = strlen (dogechat_home) + 64; fifo_filename = malloc (filename_length); snprintf (fifo_filename, filename_length, "%s/%s%d", dogechat_home, FIFO_FILENAME_PREFIX, (int) getpid()); } fifo_fd = -1; /* create FIFO pipe, writable for user only */ if (mkfifo (fifo_filename, 0600) == 0) { /* open FIFO pipe in read-only, non-blocking mode */ if ((fifo_fd = open (fifo_filename, O_RDONLY | O_NONBLOCK)) != -1) { if ((dogechat_fifo_plugin->debug >= 1) || !fifo_quiet) { dogechat_printf (NULL, _("%s: pipe opened (file: %s)"), FIFO_PLUGIN_NAME, fifo_filename); } fifo_fd_hook = dogechat_hook_fd (fifo_fd, 1, 0, 0, &fifo_read, NULL); } else dogechat_printf (NULL, _("%s%s: unable to open pipe (%s) for " "reading"), dogechat_prefix ("error"), FIFO_PLUGIN_NAME, fifo_filename); } else { dogechat_printf (NULL, _("%s%s: unable to create pipe for remote " "control (%s): error %d %s"), dogechat_prefix ("error"), FIFO_PLUGIN_NAME, fifo_filename, errno, strerror (errno)); } } } }
int xfer_network_connect (struct t_xfer *xfer) { int flags; if (xfer->type == XFER_TYPE_CHAT_SEND) xfer->status = XFER_STATUS_WAITING; else xfer->status = XFER_STATUS_CONNECTING; if (XFER_IS_SEND(xfer->type)) { /* create socket */ if (xfer->sock < 0) { xfer->sock = socket (xfer->local_address->sa_family, SOCK_STREAM, 0); if (xfer->sock < 0) return 0; } /* listen to socket */ flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; if (fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK) == -1) return 0; if (listen (xfer->sock, 1) == -1) return 0; if (fcntl (xfer->sock, F_SETFL, flags) == -1) return 0; xfer->hook_fd = dogechat_hook_fd (xfer->sock, 1, 0, 0, &xfer_network_fd_cb, xfer); /* add timeout */ if (dogechat_config_integer (xfer_config_network_timeout) > 0) { xfer->hook_timer = dogechat_hook_timer (dogechat_config_integer (xfer_config_network_timeout) * 1000, 0, 1, &xfer_network_timer_cb, xfer); } } /* for chat receiving, connect to listening host */ if (xfer->type == XFER_TYPE_CHAT_RECV) { xfer->hook_connect = dogechat_hook_connect (xfer->proxy, xfer->remote_address_str, xfer->port, 1, 0, NULL, NULL, 0, "NONE", NULL, &xfer_network_connect_chat_recv_cb, xfer); } /* for file receiving, connection is made in child process (blocking) */ return 1; }
int xfer_network_connect_chat_recv_cb (void *data, int status, int gnutls_rc, int sock, const char *error, const char *ip_address) { struct t_xfer *xfer; int flags; /* make C compiler happy */ (void) gnutls_rc; (void) ip_address; xfer = (struct t_xfer*)data; dogechat_unhook (xfer->hook_connect); xfer->hook_connect = NULL; /* connection OK? */ if (status == DOGECHAT_HOOK_CONNECT_OK) { xfer->sock = sock; flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; if (fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK) == -1) { dogechat_printf (NULL, _("%s%s: unable to set option \"nonblock\" " "for socket: error %d %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, errno, strerror (errno)); close (xfer->sock); xfer->sock = -1; xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } xfer->hook_fd = dogechat_hook_fd (xfer->sock, 1, 0, 0, &xfer_chat_recv_cb, xfer); xfer_chat_open_buffer (xfer); xfer->status = XFER_STATUS_ACTIVE; xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } /* connection error */ switch (status) { case DOGECHAT_HOOK_CONNECT_ADDRESS_NOT_FOUND: dogechat_printf (NULL, (xfer->proxy && xfer->proxy[0]) ? _("%s%s: proxy address \"%s\" not found") : _("%s%s: address \"%s\" not found"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, xfer->remote_address_str); break; case DOGECHAT_HOOK_CONNECT_IP_ADDRESS_NOT_FOUND: dogechat_printf (NULL, (xfer->proxy && xfer->proxy[0]) ? _("%s%s: proxy IP address not found") : _("%s%s: IP address not found"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_CONNECTION_REFUSED: dogechat_printf (NULL, (xfer->proxy && xfer->proxy[0]) ? _("%s%s: proxy connection refused") : _("%s%s: connection refused"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_PROXY_ERROR: dogechat_printf (NULL, _("%s%s: proxy fails to establish connection to " "server (check username/password if used and if " "server address/port is allowed by proxy)"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_LOCAL_HOSTNAME_ERROR: dogechat_printf (NULL, _("%s%s: unable to set local hostname/IP"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_MEMORY_ERROR: dogechat_printf (NULL, _("%s%s: not enough memory"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_TIMEOUT: dogechat_printf (NULL, _("%s%s: timeout"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; case DOGECHAT_HOOK_CONNECT_SOCKET_ERROR: dogechat_printf (NULL, _("%s%s: unable to create socket"), dogechat_prefix ("error"), XFER_PLUGIN_NAME); break; default: dogechat_printf (NULL, _("%s%s: unable to connect: unexpected error (%d)"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, status); break; } if (error && error[0]) { dogechat_printf (NULL, _("%s%s: error: %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, error); } xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; }
int xfer_network_fd_cb (void *arg_xfer, int fd) { struct t_xfer *xfer; int sock, flags, error; struct sockaddr_storage addr; socklen_t length; char str_address[NI_MAXHOST]; /* make C compiler happy */ (void) fd; length = sizeof (addr); memset (&addr, 0, length); xfer = (struct t_xfer *)arg_xfer; if (xfer->status == XFER_STATUS_CONNECTING) { if (xfer->type == XFER_TYPE_FILE_SEND) { xfer->last_activity = time (NULL); sock = accept (xfer->sock, (struct sockaddr *) &addr, &length); error = errno; dogechat_unhook (xfer->hook_fd); xfer->hook_fd = NULL; close (xfer->sock); xfer->sock = -1; if (sock < 0) { dogechat_printf (NULL, _("%s%s: unable to create socket for sending " "file: error %d %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, error, strerror (error)); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } xfer->sock = sock; flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; if (fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK) == -1) { dogechat_printf (NULL, _("%s%s: unable to set option \"nonblock\" " "for socket: error %d %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, errno, strerror (errno)); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } error = getnameinfo ((struct sockaddr *)&addr, length, str_address, sizeof (str_address), NULL, 0, NI_NUMERICHOST); if (error != 0) { snprintf (str_address, sizeof (str_address), "error: %s", gai_strerror (error)); } xfer_set_remote_address (xfer, (struct sockaddr *)&addr, length, str_address); xfer->status = XFER_STATUS_ACTIVE; xfer->start_transfer = time (NULL); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); xfer_network_send_file_fork (xfer); } } if (xfer->status == XFER_STATUS_WAITING) { if (xfer->type == XFER_TYPE_CHAT_SEND) { length = sizeof (addr); sock = accept (xfer->sock, (struct sockaddr *) &addr, &length); error = errno; dogechat_unhook (xfer->hook_fd); xfer->hook_fd = NULL; close (xfer->sock); xfer->sock = -1; if (sock < 0) { dogechat_printf (NULL, _("%s%s: unable to create socket for sending " "file: error %d %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, error, strerror (error)); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } xfer->sock = sock; flags = fcntl (xfer->sock, F_GETFL); if (flags == -1) flags = 0; if (fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK) == -1) { dogechat_printf (NULL, _("%s%s: unable to set option \"nonblock\" " "for socket: error %d %s"), dogechat_prefix ("error"), XFER_PLUGIN_NAME, errno, strerror (errno)); xfer_close (xfer, XFER_STATUS_FAILED); xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); return DOGECHAT_RC_OK; } error = getnameinfo ((struct sockaddr *)&addr, length, str_address, sizeof (str_address), NULL, 0, NI_NUMERICHOST); if (error != 0) { snprintf (str_address, sizeof (str_address), "error: %s", gai_strerror (error)); } xfer_set_remote_address (xfer, (struct sockaddr *)&addr, length, str_address); xfer->status = XFER_STATUS_ACTIVE; xfer_buffer_refresh (DOGECHAT_HOTLIST_MESSAGE); xfer->hook_fd = dogechat_hook_fd (xfer->sock, 1, 0, 0, &xfer_chat_recv_cb, xfer); xfer_chat_open_buffer (xfer); } } return DOGECHAT_RC_OK; }