From: Pekka Riikonen Date: Tue, 18 Jul 2006 17:07:28 +0000 (+0000) Subject: Added UDP network support. X-Git-Tag: 1.2.beta1~721 X-Git-Url: http://git.silc.fi/gitweb/?a=commitdiff_plain;h=c14dc73816933bb82e63a2ffbc90eaca9c9895d3;p=runtime.git Added UDP network support. --- diff --git a/lib/silcutil/silcnet.h b/lib/silcutil/silcnet.h index 08168fa3..41a357e9 100644 --- a/lib/silcutil/silcnet.h +++ b/lib/silcutil/silcnet.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,8 +22,9 @@ * DESCRIPTION * * SILC Net API provides various network routines for applications. It - * can be used to create TCP/IP connections and servers. Various utility - * functions for resolving various information is also provided. + * can be used to create TCP/IP and UDP/IP connections and listeners. + * Various utility functions for resolving various information is also + * provided. * * On WIN32 systems the SILC Net API must initialized by calling the * silc_net_win32_init and uninitialized when the application ends by @@ -37,20 +38,20 @@ /* Prototypes */ -/****s* silcutil/SilcNetAPI/SilcNetServer +/****s* silcutil/SilcNetAPI/SilcNetListener * * NAME * - * typedef struct SilcNetServerStruct *SilcNetServer; + * typedef struct SilcNetListenerStruct *SilcNetListener; * * DESCRIPTION * - * The network server (daemon, listener, etc.) context. This context - * is created with the silc_net_create_server function and destroyed - * with silc_net_close_server function. + * The network listenr context. This context is created with the + * silc_net_create_listener function and destroyed with + * silc_net_close_listener function. * ***/ -typedef struct SilcNetServerStruct *SilcNetServer; +typedef struct SilcNetListenerStruct *SilcNetListener; /****d* silcutil/SilcNetAPI/SilcNetStatus * @@ -86,64 +87,68 @@ typedef enum { * * DESCRIPTION * - * A callback function of this type is returned by silc_net_create_server - * and silc_net_connect_async functions. For silc_net_create_server this - * callback means that new incoming connection was accepted, and the - * `stream' is the socket stream representing the socket connection. + * A callback of this type is returned by silc_net_tcp_create_listener, + * silc_net_udp_create_listener, silc_net_tcp_connect and + * silc_net_udp_connect functions. For silc_net_tcp_create_listener + * and silc_net_udp_create_listener this callback means that new incoming + * connection was accepted, and the `stream' is the socket stream + * representing the socket connection. * - * For silc_net_connect_async this means that we have connected to the - * remote host and the `stream' is the socket stream for the socket - * connection. The SILC Stream API (such as silc_stream_read, etc.) - * can be used to read and write to the stream. The created stream - * is socket stream so various SilcSocketStream API functions can be - * used with the `stream'. + * For silc_net_tcp_connect and silc_net_udp_connect this means that we + * have connected to the remote host and the `stream' is the socket + * stream for the socket connection. The SILC Stream API (such as + * silc_stream_read, etc.) can be used to read and write to the stream. + * The created stream is socket stream so various SilcSocketStream API + * functions can be used with the `stream'. * ***/ typedef void (*SilcNetCallback)(SilcNetStatus status, SilcStream stream, void *context); -/****f* silcutil/SilcNetAPI/silc_net_create_server +/****f* silcutil/SilcNetAPI/silc_net_tcp_create_listener * * SYNOPSIS * - * SilcNetServer - * silc_net_create_server(const char **local_ip_addr, - * SilcUInt32 local_ip_count, - * int port, SilcBool require_fqdn, - * SilcSchedule schedule, - * SilcNetCallback callback, void *context); + * SilcNetListener + * silc_net_tcp_create_listener(const char **local_ip_addr, + * SilcUInt32 local_ip_count, + * int port, SilcBool require_fqdn, + * SilcSchedule schedule, + * SilcNetCallback callback, void *context); * * DESCRIPTION * - * This function creates server or daemon or listener etc. This is used - * to create network listener for incoming connections, and `callback' - * will be called everytime new connection is received. If `local_ip_addr' - * is NULL any address is used. If provided it can be used bind the - * server to `local_ip_count' many IP addresses provided in `local_ip_addr' - * table. On success returns the SilcNetServer context, or NULL on error. - * If `require_fqdn' is TRUE the server will require that the incoming + * This function creates TCP listener etc. This is used to create network + * listener for incoming connections, and `callback' will be called + * everytime new connection is received. If `local_ip_addr' is NULL any + * address is used. If provided it can be used bind the listener to + * `local_ip_count' many IP addresses provided in `local_ip_addr' table. + * On success returns the SilcNetListener context, or NULL on error. + * If `require_fqdn' is TRUE the listener will require that the incoming * connection has FQDN to be able to connect. * ***/ -SilcNetServer -silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, - int port, SilcBool require_fqdn, SilcSchedule schedule, - SilcNetCallback callback, void *context); +SilcNetListener +silc_net_tcp_create_listener(const char **local_ip_addr, + SilcUInt32 local_ip_count, + int port, SilcBool require_fqdn, + SilcSchedule schedule, + SilcNetCallback callback, void *context); -/****f* silcutil/SilcNetAPI/silc_net_close_server +/****f* silcutil/SilcNetAPI/silc_net_close_listener * * SYNOPSIS * - * void silc_net_close_server(SilcNetServer server); + * void silc_net_close_listener(SilcNetListener listener); * * DESCRIPTION * - * Closes the network server listener indicated by `server'. + * Closes the network listener indicated by `listener'. * ***/ -void silc_net_close_server(SilcNetServer server); +void silc_net_close_listener(SilcNetListener listener); -/****f* silcutil/SilcNetAPI/silc_net_connect +/****f* silcutil/SilcNetAPI/silc_net_tcp_connect * * SYNOPSIS * @@ -179,12 +184,96 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, SilcNetCallback callback, void *context); -SilcAsyncOperation silc_net_udp_connect(const char *local_ip_addr, - const char *remote_ip_addr, - int remote_port, - SilcSchedule schedule, - SilcNetCallback callback, - void *context); +/****f* silcutil/SilcNetAPI/silc_net_udp_connect + * + * SYNOPSIS + * + * SilcStream + * silc_net_udp_connect(const char *local_ip_addr, int local_port, + * const char *remote_ip_addr, int remote_port, + * SilcSchedule schedule); + * + * DESCRIPTION + * + * This function creates UDP stream. The UDP stream is bound to the + * `local_ip_addr' if it is specified. The `local_port' must always be + * specified. If the `remote_ip_addr' and `remote_port' is also provided, + * packets may be sent to that address using silc_stream_write function + * and packets may be received using silc_stream_read function. + * + * If the remote address is not provided then packets may only be received + * by using silc_net_udp_receive and sent only by using the function + * silc_net_udp_send. + * + * To receive packets the silc_stream_set_notifier must be called for the + * returned SilcStream. The packets are always received in the notifier + * callback when the SILC_STREAM_CAN_READ is returned to the callback + * To read the packet use silc_stream_read if the remote address was + * provided, and silc_net_udp_receive if it was not. + * + * EXAMPLE + * + * SilcStream udpstream; + * + * // Create UDP stream and prepare to receive packets + * udpstream = silc_net_udp_connect("10.2.1.7", 5000, + * "10.2.1.100, 5000, schedule); + * silc_stream_set_notifier(udpstream, schedule, receive_callback, context); + * + * // Send packet to remote host + * silc_stream_write(udpstream, data, data_len); + * + ***/ +SilcStream +silc_net_udp_connect(const char *local_ip_addr, int local_port, + const char *remote_ip_addr, int remote_port, + SilcSchedule schedule); + +/****f* silcutil/SilcNetAPI/silc_net_udp_receive + * + * SYNOPSIS + * + * int + * silc_net_udp_receive(SilcStream stream, char *remote_ip_addr, + * SilcUInt32 remote_ip_addr_size, int *remote_port, + * unsigned char *ret_data, SilcUInt32 data_size) + * + * DESCRIPTION + * + * Receive a UDP packet from the `stream'. The IP address and port of + * the sender is returned into `remote_ip_addr' buffer and `remote_port' + * pointer. The packet data is returned into the `ret_data' buffer. + * + * Returns the length of the packet, or -1 on error or 0 in case of EOF. + * + ***/ +int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr, + SilcUInt32 remote_ip_addr_size, int *remote_port, + unsigned char *ret_data, SilcUInt32 data_size); + +/****f* silcutil/SilcNetAPI/silc_net_udp_send + * + * SYNOPSIS + * + * void silc_net_udp_send(SilcStream stream, + * const char *remote_ip_addr, int remote_port, + * const unsigned char *data, SilcUInt32 data_len); + * + * DESCRIPTION + * + * Sends an UDP packet to remote host `remote_ip_addr' on `remote_port'. + * This may be used with UDP streams that are not connected to any + * specific remote host. With those stream silc_stream_write cannot be + * used. In those cases, this function must be used. This may also be + * used even if the stream is connected. + * + * This function always succeeds, however there is no guarantee that the + * packet is delivered, as UDP is unreliable transport protocol. + * + ***/ +void silc_net_udp_send(SilcStream stream, + const char *remote_ip_addr, int remote_port, + const unsigned char *data, SilcUInt32 data_len); /****f* silcutil/SilcNetAPI/silc_net_close_connection * @@ -344,8 +433,8 @@ typedef void (*SilcNetResolveCallback)(const char *result, void *context); * address also. * ***/ -SilcBool silc_net_gethostbyname(const char *name, SilcBool prefer_ipv6, char *address, - SilcUInt32 address_len); +SilcBool silc_net_gethostbyname(const char *name, SilcBool prefer_ipv6, + char *address, SilcUInt32 address_len); /****f* silcutil/SilcNetAPI/silc_net_gethostbyname_async * @@ -390,7 +479,8 @@ void silc_net_gethostbyname_async(const char *name, * This is synchronous function and will block the calling process. * ***/ -SilcBool silc_net_gethostbyaddr(const char *addr, char *name, SilcUInt32 name_len); +SilcBool silc_net_gethostbyaddr(const char *addr, char *name, + SilcUInt32 name_len); /****f* silcutil/SilcNetAPI/silc_net_gethostbyaddr_async * @@ -418,7 +508,8 @@ void silc_net_gethostbyaddr_async(const char *addr, * * SYNOPSIS * - * SilcBool silc_net_check_host_by_sock(int sock, char **hostname, char **ip); + * SilcBool silc_net_check_host_by_sock(int sock, char **hostname, + * char **ip); * * DESCRIPTION * @@ -432,7 +523,8 @@ SilcBool silc_net_check_host_by_sock(int sock, char **hostname, char **ip); * * SYNOPSIS * - * SilcBool silc_net_check_local_by_sock(int sock, char **hostname, char **ip); + * SilcBool silc_net_check_local_by_sock(int sock, char **hostname, + * char **ip); * * DESCRIPTION * diff --git a/lib/silcutil/silcnet_i.h b/lib/silcutil/silcnet_i.h index 0dce8484..1da16a97 100644 --- a/lib/silcutil/silcnet_i.h +++ b/lib/silcutil/silcnet_i.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 Pekka Riikonen + Copyright (C) 2005 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,8 +24,8 @@ #error "Do not include this header directly" #endif -/* Net server context */ -struct SilcNetServerStruct { +/* Net listenrr context */ +struct SilcNetListenerStruct { SilcSchedule schedule; SilcNetCallback callback; void *context; diff --git a/lib/silcutil/silcsocketstream.c b/lib/silcutil/silcsocketstream.c index 39adeb13..b599dabf 100644 --- a/lib/silcutil/silcsocketstream.c +++ b/lib/silcutil/silcsocketstream.c @@ -24,6 +24,7 @@ #define SILC_IS_SOCKET_STREAM(s) (s->ops == &silc_socket_stream_ops) const SilcStreamOps silc_socket_stream_ops; +const SilcStreamOps silc_socket_udp_stream_ops; /* Platform specific functions */ int silc_socket_stream_read(SilcStream stream, unsigned char *buf, @@ -32,6 +33,10 @@ int silc_socket_stream_write(SilcStream stream, const unsigned char *data, SilcUInt32 data_len); SilcBool silc_socket_stream_close(SilcStream stream); void silc_socket_stream_destroy(SilcStream stream); +int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf, + SilcUInt32 buf_len); +int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data, + SilcUInt32 data_len); /* Internal async host lookup context. */ typedef struct { @@ -161,13 +166,14 @@ static void silc_socket_host_lookup_abort(SilcAsyncOperation op, /******************************* Public API *********************************/ -/* Creates socket stream */ +/* Creates TCP socket stream */ SilcAsyncOperation -silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn, - SilcSchedule schedule, - SilcSocketStreamCallback callback, - void *context) +silc_socket_tcp_stream_create(int sock, SilcBool lookup, + SilcBool require_fqdn, + SilcSchedule schedule, + SilcSocketStreamCallback callback, + void *context) { SilcSocketStream stream; SilcSocketHostLookup l; @@ -179,7 +185,7 @@ silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn, return NULL; } - SILC_LOG_DEBUG(("Creating new socket stream %p", stream)); + SILC_LOG_DEBUG(("Creating TCP socket stream %p", stream)); stream->ops = &silc_socket_stream_ops; stream->sock = sock; @@ -223,6 +229,27 @@ silc_socket_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn, } } +/* Creates UDP socket stream */ + +SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6, + SilcSchedule schedule) +{ + SilcSocketStream stream; + + stream = silc_calloc(1, sizeof(*stream)); + if (!stream) + return NULL; + + SILC_LOG_DEBUG(("Creating UDP socket stream %p", stream)); + + stream->ops = &silc_socket_udp_stream_ops; + stream->sock = sock; + stream->schedule = schedule; + stream->ipv6 = ipv6; + + return (SilcStream)stream; +} + /* Returns socket stream information */ SilcBool silc_socket_stream_get_info(SilcStream stream, @@ -438,3 +465,12 @@ const SilcStreamOps silc_socket_stream_ops = silc_socket_stream_notifier, silc_socket_stream_get_schedule, }; +const SilcStreamOps silc_socket_udp_stream_ops = +{ + silc_socket_udp_stream_read, + silc_socket_udp_stream_write, + silc_socket_stream_close, + silc_socket_stream_destroy, + silc_socket_stream_notifier, + silc_socket_stream_get_schedule, +}; diff --git a/lib/silcutil/silcsocketstream.h b/lib/silcutil/silcsocketstream.h index 08dbbcc1..8aa084d9 100644 --- a/lib/silcutil/silcsocketstream.h +++ b/lib/silcutil/silcsocketstream.h @@ -80,20 +80,20 @@ typedef enum { typedef void (*SilcSocketStreamCallback)(SilcSocketStreamStatus status, SilcStream stream, void *context); -/****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_create +/****f* silcutil/SilcSocketStreamAPI/silc_socket_tcp_stream_create * * SYNOPSIS * * SilcAsyncOperation - * silc_socket_stream_create(int sock, SilcBool lookup, - * SilcBool require_fqdn, - * SilcSchedule schedule, - * SilcSocketStreamCallback callback, - * void *context); + * silc_socket_tcp_stream_create(int sock, SilcBool lookup, + * SilcBool require_fqdn, + * SilcSchedule schedule, + * SilcSocketStreamCallback callback, + * void *context); * * DESCRIPTION * - * Creates new socket stream of the socket connection indicated by `sock'. + * Creates TCP socket stream of the TCP connection indicated by `sock'. * The stream can be destroyed by calling the silc_stream_destroy. Data * can be sent and received from the stream by calling silc_stream_write * and silc_stream_read. The creation process is asynchronous since @@ -111,25 +111,40 @@ typedef void (*SilcSocketStreamCallback)(SilcSocketStreamStatus status, * ***/ SilcAsyncOperation -silc_socket_stream_create(int sock, SilcBool lookup, - SilcBool require_fqdn, - SilcSchedule schedule, - SilcSocketStreamCallback callback, - void *context); - -SilcAsyncOperation silc_socket_tcp_stream_create(int sock, SilcBool lookup, SilcBool require_fqdn, SilcSchedule schedule, SilcSocketStreamCallback callback, void *context); -SilcAsyncOperation -silc_socket_udp_stream_create(int sock, SilcBool lookup, - SilcBool require_fqdn, - SilcSchedule schedule, - SilcSocketStreamCallback callback, - void *context); +/****f* silcutil/SilcSocketStreamAPI/silc_socket_udp_stream_create + * + * SYNOPSIS + * + * SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6, + * SilcSchedule schedule); + * + * DESCRIPTION + * + * Creates UDP socket stream of the UDP connection indicated by `sock'. + * The stream can be destroyed by calling the silc_stream_destroy. + * + * Note that, UDP packets may be read only through the notifier + * callback (see silc_stream_set_notifier), when SILC_STREAM_CAN_READ + * is returned to the callback. Because of this the notifier callback + * must be set. + * + * Note that, UDP packet sending using silc_stream_write and receiving + * with silc_stream_read works only if the `sock' is a UDP socket in a + * connected state. If it is not the silc_net_udp_send function and + * silc_net_udp_receive functions must be used. The SILC_STREAM_CAN_WRITE + * is never returned to the notifier callback. + * + * This function returns the created SilcStream or NULL on error. + * + ***/ +SilcStream silc_socket_udp_stream_create(int sock, SilcBool ipv6, + SilcSchedule schedule); /****f* silcutil/SilcSocketStreamAPI/silc_socket_stream_get_info * diff --git a/lib/silcutil/silcsocketstream_i.h b/lib/silcutil/silcsocketstream_i.h index 7a4998cc..46c5ef29 100644 --- a/lib/silcutil/silcsocketstream_i.h +++ b/lib/silcutil/silcsocketstream_i.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2005 Pekka Riikonen + Copyright (C) 2005 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,6 +52,7 @@ struct SilcSocketStreamStruct { SilcSocketQos qos; SilcStreamNotifier notifier; void *notifier_context; + unsigned int ipv6 : 1; }; #endif /* SILCSOCKETSTREAM_I_H */ diff --git a/lib/silcutil/unix/silcunixnet.c b/lib/silcutil/unix/silcunixnet.c index 1d3364a5..c6a020d7 100644 --- a/lib/silcutil/unix/silcunixnet.c +++ b/lib/silcutil/unix/silcunixnet.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ typedef union { } SilcSockaddr; static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, - int port) + int port) { int len; @@ -86,19 +86,19 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, static void silc_net_accept_stream(SilcSocketStreamStatus status, SilcStream stream, void *context) { - SilcNetServer server = context; + SilcNetListener listener = context; if (status != SILC_SOCKET_OK) return; - server->callback(SILC_NET_OK, stream, server->context); + listener->callback(SILC_NET_OK, stream, listener->context); } /* Accept incoming connection and notify upper layer */ SILC_TASK_CALLBACK(silc_net_accept) { - SilcNetServer server = context; + SilcNetListener listener = context; int sock; SILC_LOG_DEBUG(("Accepting new connection")); @@ -112,45 +112,47 @@ SILC_TASK_CALLBACK(silc_net_accept) silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); /* Create socket stream */ - silc_socket_stream_create(sock, TRUE, server->require_fqdn, schedule, - silc_net_accept_stream, server); + silc_socket_tcp_stream_create(sock, TRUE, listener->require_fqdn, schedule, + silc_net_accept_stream, listener); } -/* Create network listener */ +/* Create TCP network listener */ -SilcNetServer -silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, - int port, SilcBool require_fqdn, SilcSchedule schedule, - SilcNetCallback callback, void *context) +SilcNetListener +silc_net_tcp_create_listener(const char **local_ip_addr, + SilcUInt32 local_ip_count, + int port, SilcBool require_fqdn, + SilcSchedule schedule, + SilcNetCallback callback, void *context) { - SilcNetServer netserver = NULL; + SilcNetListener listener = NULL; SilcSockaddr server; int i, sock, rval; const char *ipany = "0.0.0.0"; - SILC_LOG_DEBUG(("Creating new network listener")); + SILC_LOG_DEBUG(("Creating TCP listener")); if (port < 1 || !schedule || !callback) goto err; - netserver = silc_calloc(1, sizeof(*netserver)); - if (!netserver) { + listener = silc_calloc(1, sizeof(*listener)); + if (!listener) { callback(SILC_NET_NO_MEMORY, NULL, context); return NULL; } - netserver->schedule = schedule; - netserver->callback = callback; - netserver->context = context; + listener->schedule = schedule; + listener->callback = callback; + listener->context = context; if (local_ip_count > 0) { - netserver->socks = silc_calloc(local_ip_count, sizeof(*netserver->socks)); - if (!netserver->socks) { + listener->socks = silc_calloc(local_ip_count, sizeof(*listener->socks)); + if (!listener->socks) { callback(SILC_NET_NO_MEMORY, NULL, context); return NULL; } } else { - netserver->socks = silc_calloc(1, sizeof(*netserver->socks)); - if (!netserver->socks) { + listener->socks = silc_calloc(1, sizeof(*listener->socks)); + if (!listener->socks) { callback(SILC_NET_NO_MEMORY, NULL, context); return NULL; } @@ -183,7 +185,7 @@ silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, goto err; } - /* Bind the server socket */ + /* Bind the listener socket */ rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); if (rval < 0) { SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno))); @@ -201,39 +203,220 @@ silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, silc_net_set_socket_nonblock(sock); /* Schedule for incoming connections */ - silc_schedule_task_add_fd(schedule, sock, silc_net_accept, netserver); + silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener); - SILC_LOG_DEBUG(("Network listener created, fd=%d", sock)); - netserver->socks[i] = sock; - netserver->socks_count++; + SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock)); + listener->socks[i] = sock; + listener->socks_count++; } - return netserver; + return listener; err: if (callback) callback(SILC_NET_ERROR, NULL, context); - if (netserver) - silc_net_close_server(netserver); + if (listener) + silc_net_close_listener(listener); return NULL; } /* Close network listener */ -void silc_net_close_server(SilcNetServer server) +void silc_net_close_listener(SilcNetListener listener) { int i; SILC_LOG_DEBUG(("Closing network listener")); - for (i = 0; i < server->socks_count; i++) { - silc_schedule_task_del_by_fd(server->schedule, server->socks[i]); - shutdown(server->socks[i], 2); - close(server->socks[i]); + for (i = 0; i < listener->socks_count; i++) { + silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]); + shutdown(listener->socks[i], 2); + close(listener->socks[i]); } - silc_free(server->socks); - silc_free(server); + silc_free(listener->socks); + silc_free(listener); +} + +/* Create UDP stream */ + +SilcStream +silc_net_udp_connect(const char *local_ip_addr, int local_port, + const char *remote_ip_addr, int remote_port, + SilcSchedule schedule) +{ + SilcStream stream; + SilcSockaddr server; + int sock = -1, rval; + const char *ipany = "0.0.0.0"; + + SILC_LOG_DEBUG(("Creating UDP stream")); + + if (local_port < 1 || !schedule) + goto err; + + /* Bind to local addresses */ + SILC_LOG_DEBUG(("Binding to local address %s", + local_ip_addr ? local_ip_addr : ipany)); + + /* Set sockaddr for server */ + if (!silc_net_set_sockaddr(&server, local_ip_addr ? local_ip_addr : ipany, + local_port)) + goto err; + + /* Create the socket */ + sock = socket(server.sin.sin_family, SOCK_DGRAM, 0); + if (sock < 0) { + SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno))); + goto err; + } + + /* Set the socket options */ + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#ifdef SO_REUSEPORT + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEPORT, 1); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#endif /* SO_REUSEPORT */ + + /* Bind the listener socket */ + rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); + if (rval < 0) { + SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno))); + goto err; + } + + /* Set socket to non-blocking mode */ + silc_net_set_socket_nonblock(sock); + + /* Set to connected state if remote address is provided. */ + if (remote_ip_addr && remote_port) { + if (!silc_net_set_sockaddr(&server, remote_ip_addr, remote_port)) + goto err; + + rval = connect(sock, &server.sa, SIZEOF_SOCKADDR(server)); + if (rval < 0) { + SILC_LOG_DEBUG(("Cannot connect UDP stream: %s", strerror(errno))); + goto err; + } + } + + /* Set send and receive buffer size */ +#ifdef SO_SNDBUF + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#endif /* SO_SNDBUF */ +#ifdef SO_RCVBUF + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#endif /* SO_RCVBUF */ + + /* Encapsulate into socket stream */ + stream = + silc_socket_udp_stream_create(sock, local_ip_addr ? + silc_net_is_ip6(local_ip_addr) : FALSE, + schedule); + if (!stream) + goto err; + + SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock)); + return stream; + + err: + if (sock != -1) + close(sock); + return NULL; +} + +/* Receive UDP packet */ + +int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr, + SilcUInt32 remote_ip_addr_size, int *remote_port, + unsigned char *ret_data, SilcUInt32 data_size) +{ + SilcSocketStream sock = stream; + SilcSockaddr s; + struct sockaddr *from; + int len, flen; + + SILC_LOG_DEBUG(("Reading data from UDP socket %d", sock->sock)); + + if (remote_ip_addr && remote_port) { + if (sock->ipv6) { + from = (struct sockaddr *)&s.sin6; + flen = sizeof(s.sin6); + } else { + from = (struct sockaddr *)&s.sin; + flen = sizeof(s.sin); + } + len = recvfrom(sock->sock, ret_data, data_size, 0, from, &flen); + } else + len = recv(sock->sock, ret_data, data_size, 0); + + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) { + SILC_LOG_DEBUG(("Could not read immediately, will do it later")); + silc_schedule_set_listen_fd(sock->schedule, sock->sock, + SILC_TASK_READ, FALSE); + return -1; + } + SILC_LOG_DEBUG(("Cannot read from UDP socket: %d:%s", + sock->sock, strerror(errno))); + silc_schedule_unset_listen_fd(sock->schedule, sock->sock); + sock->sock_error = errno; + return -2; + } + + SILC_LOG_DEBUG(("Read %d bytes", len)); + + if (!len) + silc_schedule_unset_listen_fd(sock->schedule, sock->sock); + + /* Return remote address */ + if (remote_ip_addr && remote_port) { + if (sock->ipv6) { + *remote_port = ntohs(s.sin6.sin6_port); + inet_ntop(AF_INET6, &s.sin6.sin6_addr, remote_ip_addr, + remote_ip_addr_size); + } else { + *remote_port = ntohs(s.sin.sin_port); + inet_ntop(AF_INET, &s.sin.sin_addr, remote_ip_addr, + remote_ip_addr_size); + } + } + + return len; +} + +/* Send UDP packet */ + +void silc_net_udp_send(SilcStream stream, + const char *remote_ip_addr, int remote_port, + const unsigned char *data, SilcUInt32 data_len) +{ + SilcSocketStream sock = stream; + SilcSockaddr remote; + + SILC_LOG_DEBUG(("Writing data to UDP socket %d", sock->sock)); + + /* Set sockaddr for server */ + if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) + return; + + /* Send */ + sendto(sock->sock, data, data_len, 0, &remote.sa, SIZEOF_SOCKADDR(remote)); } /* Asynchronous TCP/IP connecting */ @@ -431,7 +614,7 @@ SILC_FSM_STATE(silc_net_connect_st_connected) /** Connection created */ silc_fsm_next(fsm, silc_net_connect_st_stream); - SILC_FSM_CALL((conn->sop = silc_socket_stream_create( + SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create( conn->sock, FALSE, FALSE, schedule, silc_net_connect_wait_stream, conn))); diff --git a/lib/silcutil/unix/silcunixsocketstream.c b/lib/silcutil/unix/silcunixsocketstream.c index f409689a..1eded3d3 100644 --- a/lib/silcutil/unix/silcunixsocketstream.c +++ b/lib/silcutil/unix/silcunixsocketstream.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2006 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -174,11 +174,40 @@ int silc_socket_stream_write(SilcStream stream, const unsigned char *data, return ret; } +/* Receive UDP packet. QoS is not supported. */ + +int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf, + SilcUInt32 buf_len) +{ + return silc_net_udp_receive(stream, NULL, 0, NULL, buf, buf_len); +} + +/* Send UDP packet. This always succeeds. */ + +int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data, + SilcUInt32 data_len) +{ + SilcSocketStream sock = stream; + int ret; + + SILC_LOG_DEBUG(("Writing data to UDP socket %d", sock->sock)); + + ret = send(sock->sock, data, data_len, 0); + if (ret < 0) { + /* Ignore error and return success */ + SILC_LOG_DEBUG(("Cannot write to UDP socket: %s", strerror(errno))); + return data_len; + } + + SILC_LOG_DEBUG(("Wrote data %d bytes", ret)); + return ret; +} + #if 0 /* Returns human readable socket error message */ SilcBool silc_socket_get_error(SilcStream sock, char *error, - SilcUInt32 error_len) + SilcUInt32 error_len) { char *err;