Esempio n. 1
0
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);
    }
}
Esempio n. 2
0
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;
}
Esempio n. 4
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;
}
Esempio n. 5
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);
}
Esempio n. 6
0
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;
}
Esempio n. 7
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;
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 11
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;
}
Esempio n. 12
0
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;
}
Esempio n. 14
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;
}
Esempio n. 15
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;
}
Esempio n. 16
0
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);
				}
			}
Esempio n. 17
0
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);
}
Esempio n. 18
0
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));
}