+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, err;
+
+ SILC_LOG_DEBUG(("Reading data from UDP socket %d", sock->sock));
+
+ if (remote_ip_addr && remote_port) {
+ if (sock->ipv6) {
+#ifdef HAVE_IPV6
+ from = (struct sockaddr *)&s.sin6;
+ flen = sizeof(s.sin6);
+#endif /* HAVE_IPV6 */
+ } 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 == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ silc_set_errno_posix(err);
+ if (err == WSAEWOULDBLOCK) {
+ 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,
+ silc_errno_string(silc_errno)));
+ silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+ 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) {
+#ifdef HAVE_IPV6
+ *remote_port = ntohs(s.sin6.sin6_port);
+ inet_ntop(AF_INET6, &s.sin6.sin6_addr, remote_ip_addr,
+ remote_ip_addr_size);
+#endif /* HAVE_IPV6 */
+ } else {
+ const char *ip = inet_ntoa(s.sin.sin_addr);
+ if (ip)
+ silc_snprintf(remote_ip_addr, remote_ip_addr_size, ip);
+ *remote_port = ntohs(s.sin.sin_port);
+ }
+
+ SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, *remote_port));
+ }
+
+ return len;
+}
+
+/* Send UDP packet */
+
+int 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;
+ int ret, err;
+
+ SILC_LOG_DEBUG(("Sending data to UDP socket %d", sock->sock));
+
+ /* Set sockaddr */
+ if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port))
+ return -2;
+
+ /* Send */
+ ret = sendto(sock->sock, data, data_len, 0, &remote.sa,
+ SIZEOF_SOCKADDR(remote));
+ if (ret == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ silc_set_errno_posix(err);
+ if (err == WSAEWOULDBLOCK) {
+ SILC_LOG_DEBUG(("Could not send immediately, will do it later"));
+ silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+ SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
+ return -1;
+ }
+ SILC_LOG_DEBUG(("Cannot send to UDP socket: %s",
+ silc_errno_string(silc_errno)));
+ silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
+ return -2;
+ }
+
+ SILC_LOG_DEBUG(("Sent data %d bytes", ret));
+ if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
+ SILC_TASK_WRITE)
+ silc_schedule_set_listen_fd(sock->schedule, sock->sock,
+ SILC_TASK_READ, FALSE);
+
+ return ret;
+}
+
+
+/******************************* TCP Stream *********************************/
+
+typedef struct {
+ SilcResult status;
+ SilcStream stream;
+ SilcFSMStruct fsm;
+ SilcFSMThreadStruct thread;
+ SilcAsyncOperation op;
+ SilcAsyncOperation sop;
+ char *local_ip;
+ char *remote;
+ char ip_addr[64];
+ int sock;
+ SilcNetCallback callback;
+ void *context;
+ unsigned int port : 24;
+ unsigned int retry : 7;
+ unsigned int aborted : 1;
+} *SilcNetConnect;
+
+SILC_FSM_STATE(silc_net_connect_st_start);
+SILC_FSM_STATE(silc_net_connect_st_stream);
+SILC_FSM_STATE(silc_net_connect_st_finish);
+
+static void silc_net_connect_wait_stream(SilcResult status,
+ SilcStream stream, void *context)
+{
+ SilcNetConnect conn = context;
+ conn->sop = NULL;
+ conn->status = status;
+ conn->stream = stream;
+ SILC_FSM_CALL_CONTINUE(&conn->thread);