static void send_current_file_offset_to_sender (irc_session_t *session, irc_dcc_session_t *dcc) { int sentBytes, err = 0; // we convert out irc_dcc_size_t to uint32_t, because it's defined like that in dcc... uint32_t confirmSizeNetworkOrder = htobe32(dcc->file_confirm_offset); size_t offset = sizeof(confirmSizeNetworkOrder); #ifdef ENABLE_SSL if (dcc->ssl == 0) sentBytes = socket_send(&dcc->sock, &confirmSizeNetworkOrder, offset); else { int sslError = 0; sentBytes = ssl_write_wrapper(session, dcc, &confirmSizeNetworkOrder, offset, &sslError); if (sslError == SSL_ERROR_WANT_READ) { dcc->state = LIBIRC_STATE_CONNECTED; return; } else if (sslError == SSL_ERROR_WANT_WRITE) { return; } } #else sentBytes = socket_send(&dcc->sock, &confirmSizeNetworkOrder, offset); #endif if (unlikely(sentBytes < 0)) { DBG_WARN("err send length < 0"); DBG_WARN("error msg: %s\n", strerror(errno)); err = LIBIRC_ERR_WRITE; } else if (unlikely(sentBytes == 0)) { err = LIBIRC_ERR_CLOSED; } else { if (unlikely(dcc->received_file_size == dcc->file_confirm_offset)) { DBG_OK("dcc->received_file_size == dcc->file_confirm_offset"); libirc_mutex_unlock(&session->mutex_dcc); (*dcc->cb)(session, dcc->id, 0, dcc->ctx, 0, 0); libirc_mutex_lock(&session->mutex_dcc); libirc_dcc_destroy_nolock(session, dcc->id); } else { /* Continue to receive the file */ dcc->state = LIBIRC_STATE_CONNECTED; } } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if (unlikely(err)) { libirc_mutex_unlock(&session->mutex_dcc); (*dcc->cb)(session, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock(&session->mutex_dcc); //libirc_dcc_destroy_nolock (ircsession, dcc->id); } }
static void libirc_remove_dcc_session (irc_session_t * session, irc_dcc_session_t * dcc, int lock_list) { if ( dcc->sock >= 0 ) socket_close (&dcc->sock); if ( dcc->dccsend_file_fp ) fclose (dcc->dccsend_file_fp); dcc->dccsend_file_fp = 0; libirc_mutex_destroy (&dcc->mutex_outbuf); if ( lock_list ) libirc_mutex_lock (&session->mutex_dcc); if ( session->dcc_sessions != dcc ) { irc_dcc_session_t * s; for ( s = session->dcc_sessions; s; s = s->next ) { if ( s->next == dcc ) { s->next = dcc->next; break; } } } else session->dcc_sessions = dcc->next; if ( lock_list ) libirc_mutex_unlock (&session->mutex_dcc); free (dcc); }
int irc_send_raw (irc_session_t * session, const char * format, ...) { char buf[1024]; va_list va_alist; if ( session->state != LIBIRC_STATE_CONNECTED ) { session->lasterror = LIBIRC_ERR_STATE; return 1; } va_start (va_alist, format); vsnprintf (buf, sizeof(buf), format, va_alist); va_end (va_alist); libirc_mutex_lock (&session->mutex_session); if ( (strlen(buf) + 2) >= (sizeof(session->outgoing_buf) - session->outgoing_offset) ) { libirc_mutex_unlock (&session->mutex_session); session->lasterror = LIBIRC_ERR_NOMEM; return 1; } strcpy (session->outgoing_buf + session->outgoing_offset, buf); session->outgoing_offset += strlen (buf); session->outgoing_buf[session->outgoing_offset++] = 0x0D; session->outgoing_buf[session->outgoing_offset++] = 0x0A; libirc_mutex_unlock (&session->mutex_session); return 0; }
int irc_add_select_descriptors(irc_session_t * session) { if (session->sock < 0 || session->state == LIBIRC_STATE_INIT || session->state == LIBIRC_STATE_DISCONNECTED) { session->lasterror = LIBIRC_ERR_STATE; return 1; } libirc_mutex_lock(&session->mutex_session); switch (session->state) { case LIBIRC_STATE_CONNECTING: // While connection, only out_set descriptor should be set fdwatch_set_fd(session->sock, FDW_WRITE); break; case LIBIRC_STATE_CONNECTED: // Add input descriptor if there is space in input buffer if (session->incoming_offset < (sizeof (session->incoming_buf) - 1) || (session->flags & SESSIONFL_SSL_WRITE_WANTS_READ) != 0) fdwatch_set_fd(session->sock, FDW_READ); // Add output descriptor if there is something in output buffer if (libirc_findcrlf(session->outgoing_buf, session->outgoing_offset) > 0 || (session->flags & SESSIONFL_SSL_READ_WANTS_WRITE) != 0) fdwatch_set_fd(session->sock, FDW_WRITE); break; } libirc_mutex_unlock(&session->mutex_session); libirc_dcc_add_descriptors(session); return 0; }
static void libirc_dcc_process_descriptors(irc_session_t * ircsession) { irc_dcc_session_t * dcc; /* * We need to use such a complex scheme here, because on every callback * a number of DCC sessions could be destroyed. */ libirc_mutex_lock(&ircsession->mutex_dcc); for (dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next) { switch (dcc->state) { case LIBIRC_STATE_CONNECTING: //printf("LIBIRC_STATE_CONNECTING\n"); handleConnectingState(ircsession, dcc); break; case LIBIRC_STATE_CONNECTED: //printf("LIBIRC_STATE_CONNECTED\n"); handleConnectedState(ircsession, dcc); break; case LIBIRC_STATE_CONFIRM_SIZE: //printf("LIBIRC_STATE_CONFIRM_SIZE\n"); handleConfirmSizeState(ircsession, dcc); break; case LIBIRC_STATE_WAITING_FOR_RESUME_ACK: break; default: DBG_WARN("unknown state %d at libirc_dcc_process_descriptors!", dcc->state); break; } } libirc_mutex_unlock(&ircsession->mutex_dcc); }
int irc_dcc_msg (irc_session_t * session, irc_dcc_t dccid, const char * text) { irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1); if ( !dcc ) return 1; if ( dcc->dccmode != LIBIRC_DCC_CHAT ) { session->lasterror = LIBIRC_ERR_INVAL; libirc_mutex_unlock (&session->mutex_dcc); return 1; } if ( (strlen(text) + 2) >= (sizeof(dcc->outgoing_buf) - dcc->outgoing_offset) ) { session->lasterror = LIBIRC_ERR_NOMEM; libirc_mutex_unlock (&session->mutex_dcc); return 1; } libirc_mutex_lock (&dcc->mutex_outbuf); strcpy (dcc->outgoing_buf + dcc->outgoing_offset, text); dcc->outgoing_offset += strlen (text); dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0D; dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0A; libirc_mutex_unlock (&dcc->mutex_outbuf); libirc_mutex_unlock (&session->mutex_dcc); return 0; }
static irc_dcc_session_t * libirc_find_dcc_session(irc_session_t * session, irc_dcc_t dccid, int lock_list) { irc_dcc_session_t * s, *found = 0; if (lock_list) libirc_mutex_lock(&session->mutex_dcc); for (s = session->dcc_sessions; s; s = s->next) { if (s->id == dccid) { found = s; break; } } if (found == 0 && lock_list) libirc_mutex_unlock(&session->mutex_dcc); return found; }
static irc_dcc_session_t * libirc_find_dcc_session_by_port(irc_session_t * session, unsigned short port, int lock_list) { irc_dcc_session_t * s, *found = 0; if (lock_list) libirc_mutex_lock(&session->mutex_dcc); for (s = session->dcc_sessions; s; s = s->next) { if (ntohs(s->remote_addr.sin_port) == port) { found = s; break; } } if (found == 0 && lock_list) libirc_mutex_unlock(&session->mutex_dcc); return found; }
static void libirc_dcc_add_descriptors(irc_session_t * ircsession) { irc_dcc_session_t * dcc, *dcc_next; libirc_mutex_lock(&ircsession->mutex_dcc); // Preprocessing DCC list: // - ask DCC send callbacks for data; // - remove unused DCC structures for (dcc = ircsession->dcc_sessions; dcc; dcc = dcc_next) { dcc_next = dcc->next; // Clean up unused sessions if (dcc->state == LIBIRC_STATE_REMOVED) libirc_remove_dcc_session(ircsession, dcc, 0); } for (dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next) { fdwatch_add_fd(dcc->sock); switch (dcc->state) { case LIBIRC_STATE_CONNECTING: // While connection, only out_set descriptor should be set fdwatch_set_fd(dcc->sock, FDW_WRITE); break; case LIBIRC_STATE_CONNECTED: fdwatch_set_fd(dcc->sock, FDW_READ); break; case LIBIRC_STATE_CONFIRM_SIZE: fdwatch_set_fd(dcc->sock, FDW_WRITE); break; case LIBIRC_STATE_WAITING_FOR_RESUME_ACK: break; default: DBG_WARN("unknown state at libirc_dcc_add_descriptors"); break; } } libirc_mutex_unlock(&ircsession->mutex_dcc); }
int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd) { if ( session->sock < 0 || session->state == LIBIRC_STATE_INIT || session->state == LIBIRC_STATE_DISCONNECTED ) { session->lasterror = LIBIRC_ERR_STATE; return 1; } libirc_mutex_lock (&session->mutex_session); switch (session->state) { case LIBIRC_STATE_CONNECTING: // While connection, only out_set descriptor should be set libirc_add_to_set (session->sock, out_set, maxfd); break; case LIBIRC_STATE_CONNECTED: // Add input descriptor if there is space in input buffer if ( session->incoming_offset < (sizeof (session->incoming_buf) - 1) ) libirc_add_to_set (session->sock, in_set, maxfd); // Add output descriptor if there is something in output buffer if ( libirc_findcrlf (session->outgoing_buf, session->outgoing_offset) > 0 ) libirc_add_to_set (session->sock, out_set, maxfd); break; } libirc_mutex_unlock (&session->mutex_session); libirc_dcc_add_descriptors (session, in_set, out_set, maxfd); return 0; }
static int libirc_new_dcc_session (irc_session_t * session, unsigned long ip, unsigned short port, int dccmode, void * ctx, irc_dcc_session_t ** pdcc) { irc_dcc_session_t * dcc = malloc (sizeof(irc_dcc_session_t)); if ( !dcc ) return LIBIRC_ERR_NOMEM; // setup memset (dcc, 0, sizeof(irc_dcc_session_t)); dcc->dccsend_file_fp = 0; if ( libirc_mutex_init (&dcc->mutex_outbuf) ) goto cleanup_exit_error; if ( socket_create (PF_INET, SOCK_STREAM, &dcc->sock) ) goto cleanup_exit_error; if ( !ip ) { struct sockaddr_in saddr; unsigned long arg = 1; setsockopt (dcc->sock, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, sizeof(arg)); memset (&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; memcpy (&saddr.sin_addr, &session->local_addr, sizeof(session->local_addr)); saddr.sin_port = htons (0); if ( bind (dcc->sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0 ) goto cleanup_exit_error; if ( listen (dcc->sock, 5) < 0 ) goto cleanup_exit_error; dcc->state = LIBIRC_STATE_LISTENING; } else { // make socket non-blocking, so connect() call won't block if ( socket_make_nonblocking (&dcc->sock) ) goto cleanup_exit_error; memset (&dcc->remote_addr, 0, sizeof(dcc->remote_addr)); dcc->remote_addr.sin_family = AF_INET; dcc->remote_addr.sin_addr.s_addr = htonl (ip); // what idiot came up with idea to send IP address in host-byteorder? dcc->remote_addr.sin_port = htons(port); dcc->state = LIBIRC_STATE_INIT; } dcc->dccmode = dccmode; dcc->ctx = ctx; time (&dcc->timeout); // and store it libirc_mutex_lock (&session->mutex_dcc); dcc->id = session->dcc_last_id++; dcc->next = session->dcc_sessions; session->dcc_sessions = dcc; libirc_mutex_unlock (&session->mutex_dcc); *pdcc = dcc; return 0; cleanup_exit_error: if ( dcc->sock >= 0 ) socket_close (&dcc->sock); free (dcc); return LIBIRC_ERR_SOCKET; }
static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set) { irc_dcc_session_t * dcc; /* * We need to use such a complex scheme here, because on every callback * a number of DCC sessions could be destroyed. */ libirc_mutex_lock (&ircsession->mutex_dcc); for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) { if ( dcc->state == LIBIRC_STATE_LISTENING && FD_ISSET (dcc->sock, in_set) ) { socklen_t len = sizeof(dcc->remote_addr); int nsock, err = 0; // New connection is available; accept it. if ( socket_accept (&dcc->sock, &nsock, (struct sockaddr *) &dcc->remote_addr, &len) ) err = LIBIRC_ERR_ACCEPT; // On success, change the active socket and change the state if ( err == 0 ) { // close the listen socket, and replace it by a newly // accepted socket_close (&dcc->sock); dcc->sock = nsock; dcc->state = LIBIRC_STATE_CONNECTED; } // If this is DCC chat, inform the caller about accept() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTING && FD_ISSET (dcc->sock, out_set) ) { // Now we have to determine whether the socket is connected // or the connect is failed struct sockaddr_in saddr; socklen_t slen = sizeof(saddr); int err = 0; if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 ) err = LIBIRC_ERR_CONNECT; // On success, change the state if ( err == 0 ) dcc->state = LIBIRC_STATE_CONNECTED; // If this is DCC chat, inform the caller about connect() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTED || dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( FD_ISSET (dcc->sock, in_set) ) { int length, offset = 0, err = 0; unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset; length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount); if ( length < 0 ) { err = LIBIRC_ERR_READ; } else if ( length == 0 ) { err = LIBIRC_ERR_CLOSED; if ( dcc->dccsend_file_fp ) { fclose (dcc->dccsend_file_fp); dcc->dccsend_file_fp = 0; } } else { dcc->incoming_offset += length; if ( dcc->dccmode != LIBIRC_DCC_CHAT ) offset = dcc->incoming_offset; else offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset); /* * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any * callbacks (except there is an error). We just receive * the data, and compare it with the amount sent. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( dcc->dccmode != LIBIRC_DCC_SENDFILE ) abort(); if ( dcc->incoming_offset == 4 ) { unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); // Sent size confirmed if ( dcc->file_confirm_offset == received_size ) { dcc->state = LIBIRC_STATE_CONNECTED; dcc->incoming_offset = 0; } else err = LIBIRC_ERR_WRITE; } } else { /* * If it is DCC_CHAT, we send a 0-terminated string * (which is smaller than offset). Otherwise we send * a full buffer. */ libirc_mutex_unlock (&ircsession->mutex_dcc); if ( dcc->dccmode != LIBIRC_DCC_CHAT ) { if ( dcc->dccmode != LIBIRC_DCC_RECVFILE ) abort(); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset); /* * If the session is not terminated in callback, * put the sent amount into the sent_packet_size_net_byteorder */ if ( dcc->state != LIBIRC_STATE_REMOVED ) { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; dcc->file_confirm_offset += offset; *((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); dcc->outgoing_offset = 4; } } else (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf)); libirc_mutex_lock (&ircsession->mutex_dcc); if ( dcc->incoming_offset - offset > 0 ) memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset); dcc->incoming_offset -= offset; } } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } } /* * Session might be closed (with sock = -1) after the in_set * processing, so before out_set processing we should check * for this case */ if ( dcc->state == LIBIRC_STATE_REMOVED ) continue; /* * Write bit set - we can send() something, and it won't block. */ if ( FD_ISSET (dcc->sock, out_set) ) { int length, offset, err = 0; /* * Because in some cases outgoing_buf could be changed * asynchronously (by another thread), we should lock * it. */ libirc_mutex_lock (&dcc->mutex_outbuf); offset = dcc->outgoing_offset; if ( offset > 0 ) { length = socket_send (&dcc->sock, dcc->outgoing_buf, offset); if ( length < 0 ) err = LIBIRC_ERR_WRITE; else if ( length == 0 ) err = LIBIRC_ERR_CLOSED; else { /* * If this was DCC_SENDFILE, and we just sent a packet, * change the state to wait for confirmation (and store * sent packet size) */ if ( dcc->state == LIBIRC_STATE_CONNECTED && dcc->dccmode == LIBIRC_DCC_SENDFILE ) { dcc->file_confirm_offset += offset; dcc->state = LIBIRC_STATE_CONFIRM_SIZE; libirc_mutex_unlock (&ircsession->mutex_dcc); libirc_mutex_unlock (&dcc->mutex_outbuf); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_mutex_lock (&dcc->mutex_outbuf); } if ( dcc->outgoing_offset - length > 0 ) memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length); dcc->outgoing_offset -= length; /* * If we just sent the confirmation data, change state * back. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE && dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset == 0 ) { /* * If the file is already received, we should inform * the caller, and close the session. */ if ( dcc->received_file_size == dcc->file_confirm_offset ) { libirc_mutex_unlock (&ircsession->mutex_dcc); libirc_mutex_unlock (&dcc->mutex_outbuf); (*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0); libirc_dcc_destroy_nolock (ircsession, dcc->id); } else { /* Continue to receive the file */ dcc->state = LIBIRC_STATE_CONNECTED; } } } } libirc_mutex_unlock (&dcc->mutex_outbuf); /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } } } } libirc_mutex_unlock (&ircsession->mutex_dcc); }
int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set) { char buf[256], hname[256]; if ( session->sock < 0 || session->state == LIBIRC_STATE_INIT || session->state == LIBIRC_STATE_DISCONNECTED ) { session->lasterror = LIBIRC_ERR_STATE; return 1; } libirc_dcc_process_descriptors (session, in_set, out_set); // Handle "connection succeed" / "connection failed" if ( session->state == LIBIRC_STATE_CONNECTING && FD_ISSET (session->sock, out_set) ) { // Now we have to determine whether the socket is connected // or the connect is failed struct sockaddr_in saddr, laddr; socklen_t slen = sizeof(saddr); socklen_t llen = sizeof(laddr); if ( getsockname (session->sock, (struct sockaddr*)&laddr, &llen) < 0 || getpeername (session->sock, (struct sockaddr*)&saddr, &slen) < 0 ) { // connection failed session->lasterror = LIBIRC_ERR_CONNECT; session->state = LIBIRC_STATE_DISCONNECTED; return 1; } memcpy (&session->local_addr, &laddr.sin_addr, sizeof(session->local_addr)); #if defined (ENABLE_DEBUG) if ( IS_DEBUG_ENABLED(session) ) fprintf (stderr, "[DEBUG] Detected local address: %s\n", inet_ntoa(session->local_addr)); #endif session->state = LIBIRC_STATE_CONNECTED; // Get the hostname if ( gethostname (hname, sizeof(hname)) < 0 ) strcpy (hname, "unknown"); // Prepare the data, which should be sent to the server if ( session->server_password ) { snprintf (buf, sizeof(buf), "PASS %s", session->server_password); irc_send_raw (session, buf); } snprintf (buf, sizeof(buf), "NICK %s", session->nick); irc_send_raw (session, buf); /* * RFC 1459 states that "hostname and servername are normally * ignored by the IRC server when the USER command comes from * a directly connected client (for security reasons)", therefore * we don't need them. */ snprintf (buf, sizeof(buf), "USER %s unknown unknown :%s", session->username ? session->username : "******", session->realname ? session->realname : "noname"); irc_send_raw (session, buf); return 0; } if ( session->state != LIBIRC_STATE_CONNECTED ) return 1; // Hey, we've got something to read! if ( FD_ISSET (session->sock, in_set) ) { int length, offset; unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset; length = socket_recv (&session->sock, session->incoming_buf + session->incoming_offset, amount); if ( length <= 0 ) { session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); session->state = LIBIRC_STATE_DISCONNECTED; return 1; } session->incoming_offset += length; // process the incoming data while ( (offset = libirc_findcrlf (session->incoming_buf, session->incoming_offset)) > 0 ) { #if defined (ENABLE_DEBUG) if ( IS_DEBUG_ENABLED(session) ) libirc_dump_data ("RECV", session->incoming_buf, offset); #endif // parse the string libirc_process_incoming_data (session, offset - 2); if ( session->incoming_offset - offset > 0 ) memmove (session->incoming_buf, session->incoming_buf + offset, session->incoming_offset - offset); session->incoming_offset -= offset; } } // We can write a stored buffer if ( FD_ISSET (session->sock, out_set) ) { int length; // Because outgoing_buf could be changed asynchronously, we should // lock any change libirc_mutex_lock (&session->mutex_session); length = socket_send (&session->sock, session->outgoing_buf, session->outgoing_offset); if ( length <= 0 ) { session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); session->state = LIBIRC_STATE_DISCONNECTED; libirc_mutex_unlock (&session->mutex_session); return 1; } #if defined (ENABLE_DEBUG) if ( IS_DEBUG_ENABLED(session) ) libirc_dump_data ("SEND", session->outgoing_buf, length); #endif if ( session->outgoing_offset - length > 0 ) memmove (session->outgoing_buf, session->outgoing_buf + length, session->outgoing_offset - length); session->outgoing_offset -= length; libirc_mutex_unlock (&session->mutex_session); } return 0; }
int irc_process_select_descriptors(irc_session_t * session) { if (session->sock < 0 || session->state == LIBIRC_STATE_INIT || session->state == LIBIRC_STATE_DISCONNECTED) { session->lasterror = LIBIRC_ERR_STATE; return 1; } session->lasterror = 0; libirc_dcc_process_descriptors(session); // Handle "connection succeed" / "connection failed" if (unlikely(session->state == LIBIRC_STATE_CONNECTING && fdwatch_check_fd(session->sock, FDW_WRITE))) { return handle_connecting_state(session); } if (unlikely(session->state != LIBIRC_STATE_CONNECTED)) { DBG_WARN("session->state != LIBIRC_STATE_CONNECTED"); session->state = LIBIRC_STATE_DISCONNECTED; session->lasterror = LIBIRC_ERR_STATE; return 1; } // Hey, we've got something to read! if (likely(fdwatch_check_fd(session->sock, FDW_READ))) { int offset, length = session_socket_read(session); if (unlikely(length < 0)) { if (session->lasterror == 0) session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); session->state = LIBIRC_STATE_DISCONNECTED; DBG_WARN("session->state = LIBIRC_STATE_DISCONNECTED"); return 1; } session->incoming_offset += length; // process the incoming data while ((offset = libirc_findcrlf(session->incoming_buf, session->incoming_offset)) > 0) { #if defined (ENABLE_DEBUG) if (IS_DEBUG_ENABLED(session)) libirc_dump_data("RECV", session->incoming_buf, offset); #endif // parse the string libirc_process_incoming_data(session, offset); if (session->incoming_offset - offset > 0) memmove(session->incoming_buf, session->incoming_buf + offset, session->incoming_offset - offset); session->incoming_offset -= offset; } } // We can write a stored buffer if (fdwatch_check_fd(session->sock, FDW_WRITE)) { int length; // Because outgoing_buf could be changed asynchronously, we should lock any change libirc_mutex_lock(&session->mutex_session); length = session_socket_write(session); if (unlikely(length < 0)) { if (session->lasterror == 0) session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); session->state = LIBIRC_STATE_DISCONNECTED; DBG_WARN("session->state = LIBIRC_STATE_DISCONNECTED"); libirc_mutex_unlock(&session->mutex_session); return 1; } #if defined (ENABLE_DEBUG) if (IS_DEBUG_ENABLED(session)) libirc_dump_data("SEND", session->outgoing_buf, length); #endif if (length > 0 && session->outgoing_offset - length > 0) memmove(session->outgoing_buf, session->outgoing_buf + length, session->outgoing_offset - length); session->outgoing_offset -= length; libirc_mutex_unlock(&session->mutex_session); } return 0; }
static int libirc_new_dcc_session(irc_session_t * session, unsigned long ip, unsigned short port, void * ctx, irc_dcc_session_t ** pdcc, int ssl) { irc_dcc_session_t * dcc = malloc(sizeof (irc_dcc_session_t)); if (!dcc) return LIBIRC_ERR_NOMEM; // setup memset(dcc, 0, sizeof (irc_dcc_session_t)); dcc->dccsend_file_fp = 0; if (libirc_mutex_init(&dcc->mutex_outbuf)) goto cleanup_exit_error; if (socket_create(PF_INET, SOCK_STREAM, &dcc->sock)) goto cleanup_exit_error; // make socket non-blocking, so connect() call won't block if (socket_make_nonblocking(&dcc->sock)) goto cleanup_exit_error; //optimizeSocketBufferSize(dcc); #if defined (ENABLE_SSL) dcc->ssl = 0; if (ssl) { dcc->ssl = 1; if (!isSslIntitialized()) { DBG_OK("need to init ssl context!"); int ret = initSslContext(session); if (ret != 0) { DBG_ERR("ssl context cant be inited!"); exit(-1); } } dcc->ssl_ctx = SSL_new(ssl_context); SSL_set_fd(dcc->ssl_ctx, dcc->sock); if (session->verify_callback != NULL) SSL_set_verify(dcc->ssl_ctx, SSL_VERIFY_PEER, session->verify_callback); else SSL_set_verify(dcc->ssl_ctx, SSL_VERIFY_NONE, NULL); // Since we're connecting on our own, tell openssl about it SSL_set_connect_state(dcc->ssl_ctx); } #endif memset(&dcc->remote_addr, 0, sizeof (dcc->remote_addr)); dcc->remote_addr.sin_family = AF_INET; dcc->remote_addr.sin_addr.s_addr = htonl(ip); // what idiot came up with idea to send IP address in host-byteorder? dcc->remote_addr.sin_port = htons(port); dcc->state = LIBIRC_STATE_INIT; dcc->ctx = ctx; time(&dcc->timeout); // and store it libirc_mutex_lock(&session->mutex_dcc); dcc->id = session->dcc_last_id++; dcc->next = session->dcc_sessions; session->dcc_sessions = dcc; libirc_mutex_unlock(&session->mutex_dcc); *pdcc = dcc; return 0; cleanup_exit_error: if (dcc->sock >= 0) socket_close(&dcc->sock); free(dcc); return LIBIRC_ERR_SOCKET; }
static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set) { irc_dcc_session_t * dcc; /* * We need to use such a complex scheme here, because on every callback * a number of DCC sessions could be destroyed. */ libirc_mutex_lock (&ircsession->mutex_dcc); for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) { if ( dcc->state == LIBIRC_STATE_LISTENING && FD_ISSET (dcc->sock, in_set) ) { socklen_t len = sizeof(dcc->remote_addr); int nsock, err = 0; // New connection is available; accept it. if ( socket_accept (&dcc->sock, (socket_t*)&nsock, (struct sockaddr *) &dcc->remote_addr, &len) ) err = LIBIRC_ERR_ACCEPT; // On success, change the active socket and change the state if ( err == 0 ) { // close the listen socket, and replace it by a newly // accepted socket_close (&dcc->sock); dcc->sock = nsock; dcc->state = LIBIRC_STATE_CONNECTED; } // If this is DCC chat, inform the caller about accept() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTING && FD_ISSET (dcc->sock, out_set) ) { // Now we have to determine whether the socket is connected // or the connect is failed struct sockaddr_in saddr; socklen_t slen = sizeof(saddr); int err = 0; if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 ) err = LIBIRC_ERR_CONNECT; // On success, change the state if ( err == 0 ) dcc->state = LIBIRC_STATE_CONNECTED; // If this is DCC chat, inform the caller about connect() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTED || dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( FD_ISSET (dcc->sock, in_set) ) { int length, offset = 0, err = 0; unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset; length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount); if ( length < 0 ) { err = LIBIRC_ERR_READ; } else if ( length == 0 ) { err = LIBIRC_ERR_CLOSED; if ( dcc->dccsend_file_fp ) { fclose (dcc->dccsend_file_fp); dcc->dccsend_file_fp = 0; } } else { dcc->incoming_offset += length; if ( dcc->dccmode != LIBIRC_DCC_CHAT ) offset = dcc->incoming_offset; else offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset); /* * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any * callbacks (except there is an error). We just receive * the data, and compare it with the amount sent. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( dcc->dccmode != LIBIRC_DCC_SENDFILE ) abort(); if ( dcc->incoming_offset == 4 ) { // The order is big-endian const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf; unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; // Sent size confirmed if ( dcc->file_confirm_offset == received_size ) { dcc->state = LIBIRC_STATE_CONNECTED; dcc->incoming_offset = 0; } else err = LIBIRC_ERR_WRITE; } } else { /* * If it is DCC_CHAT, we send a 0-terminated string * (which is smaller than offset). Otherwise we send * a full buffer. */ libirc_mutex_unlock (&ircsession->mutex_dcc); if ( dcc->dccmode != LIBIRC_DCC_CHAT ) { if ( dcc->dccmode != LIBIRC_DCC_RECVFILE ) abort(); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset); /* * If the session is not terminated in callback, * put the sent amount into the sent_packet_size_net_byteorder */ if ( dcc->state != LIBIRC_STATE_REMOVED ) { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; dcc->file_confirm_offset += offset; // Store as big endian dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24; dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16; dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8; dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset; dcc->outgoing_offset = 4; } } else (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf)); libirc_mutex_lock (&ircsession->mutex_dcc); if ( dcc->incoming_offset - offset > 0 ) memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset); dcc->incoming_offset -= offset; } } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } }
static void libirc_dcc_add_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set, int * maxfd) { irc_dcc_session_t * dcc, *dcc_next; time_t now = time (0); libirc_mutex_lock (&ircsession->mutex_dcc); // Preprocessing DCC list: // - ask DCC send callbacks for data; // - remove unused DCC structures for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc_next ) { dcc_next = dcc->next; // Remove timed-out sessions if ( (dcc->state == LIBIRC_STATE_CONNECTING || dcc->state == LIBIRC_STATE_INIT || dcc->state == LIBIRC_STATE_LISTENING) && now - dcc->timeout > ircsession->dcc_timeout ) { // Inform the caller about DCC timeout. // Do not inform when state is LIBIRC_STATE_INIT - session // was initiated from someone else, and callbacks aren't set yet. if ( dcc->state != LIBIRC_STATE_INIT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); if ( dcc->cb ) (*dcc->cb)(ircsession, dcc->id, LIBIRC_ERR_TIMEOUT, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } libirc_remove_dcc_session (ircsession, dcc, 0); } /* * If we're sending file, and the output buffer is empty, we need * to provide some data. */ if ( dcc->state == LIBIRC_STATE_CONNECTED && dcc->dccmode == LIBIRC_DCC_SENDFILE && dcc->dccsend_file_fp && dcc->outgoing_offset == 0 ) { int len = fread (dcc->outgoing_buf, 1, sizeof (dcc->outgoing_buf), dcc->dccsend_file_fp); if ( len <= 0 ) { int err = (len < 0 ? LIBIRC_ERR_READ : 0); libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } else dcc->outgoing_offset = len; } // Clean up unused sessions if ( dcc->state == LIBIRC_STATE_REMOVED ) libirc_remove_dcc_session (ircsession, dcc, 0); } for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) { switch (dcc->state) { case LIBIRC_STATE_LISTENING: // While listening, only in_set descriptor should be set libirc_add_to_set (dcc->sock, in_set, maxfd); break; case LIBIRC_STATE_CONNECTING: // While connection, only out_set descriptor should be set libirc_add_to_set (dcc->sock, out_set, maxfd); break; case LIBIRC_STATE_CONNECTED: // Add input descriptor if there is space in input buffer // and it is DCC chat (during DCC send, there is nothing to recv) if ( dcc->incoming_offset < sizeof(dcc->incoming_buf) - 1 ) libirc_add_to_set (dcc->sock, in_set, maxfd); // Add output descriptor if there is something in output buffer libirc_mutex_lock (&dcc->mutex_outbuf); if ( dcc->outgoing_offset > 0 ) libirc_add_to_set (dcc->sock, out_set, maxfd); libirc_mutex_unlock (&dcc->mutex_outbuf); break; case LIBIRC_STATE_CONFIRM_SIZE: /* * If we're receiving file, then WE should confirm the transferred * part (so we have to sent data). But if we're sending the file, * then RECEIVER should confirm the packet, so we have to receive * data. * * We don't need to LOCK_DCC_OUTBUF - during file transfer, buffers * can't change asynchronously. */ if ( dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset > 0 ) libirc_add_to_set (dcc->sock, out_set, maxfd); if ( dcc->dccmode == LIBIRC_DCC_SENDFILE && dcc->incoming_offset < 4 ) libirc_add_to_set (dcc->sock, in_set, maxfd); break; } } libirc_mutex_unlock (&ircsession->mutex_dcc); }
static void recv_dcc_file(irc_session_t *ircsession, irc_dcc_session_t *dcc) { int rcvdBytes, err = 0; size_t amount = LIBIRC_DCC_BUFFER_SIZE; do { #ifdef ENABLE_SSL if (dcc->ssl == 0) rcvdBytes = socket_recv(&dcc->sock, dcc->incoming_buf, amount); else { int sslError = 0; rcvdBytes = ssl_read_wrapper(ircsession, dcc, dcc->incoming_buf, amount, &sslError); if (sslError == SSL_ERROR_WANT_READ) { return; } else if (sslError == SSL_ERROR_WANT_WRITE) { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; return; } } #else rcvdBytes = socket_recv(&dcc->sock, dcc->incoming_buf, amount); #endif if (unlikely(rcvdBytes < 0)) { err = LIBIRC_ERR_READ; } else if (unlikely(rcvdBytes == 0)) { err = LIBIRC_ERR_CLOSED; } else { libirc_mutex_unlock(&ircsession->mutex_dcc); dcc->file_confirm_offset += rcvdBytes; (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, rcvdBytes); /* if DONT_CONFIRM_OFFSETS_FLAG is set dont send the file offset to the bots because some bots dont want to receive the file offsets...*/ if (cfg_get_bit(getCfg(), DONT_CONFIRM_OFFSETS_FLAG)) { dcc->state = LIBIRC_STATE_CONNECTED; } else { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; } libirc_mutex_lock(&ircsession->mutex_dcc); } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if (unlikely(err)) { libirc_mutex_unlock(&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock(&ircsession->mutex_dcc); libirc_dcc_destroy_nolock(ircsession, dcc->id); return; } } while (hasSocketPendingData(dcc)); }