Added SILC Thread Queue API
[silc.git] / lib / silcutil / win32 / silcwin32socketstream.c
1 /*
2
3   silcwin32socketstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 /************************ Static utility functions **************************/
23
24 /* The IO process callback that calls the notifier callback to upper layer. */
25
26 SILC_TASK_CALLBACK(silc_socket_stream_io)
27 {
28   SilcSocketStream stream = context;
29
30   if (!stream->notifier)
31     return;
32
33   switch (type) {
34   case SILC_TASK_READ:
35     stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
36     break;
37
38   case SILC_TASK_WRITE:
39     stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
40     break;
41
42   default:
43     break;
44   }
45 }
46
47 /**************************** Stream Operations *****************************/
48
49 /* Stream read operation */
50
51 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
52                             SilcUInt32 buf_len)
53 {
54   SilcSocketStream sock = stream;
55   SOCKET fd = sock->sock;
56   int len, argp;
57
58   SILC_LOG_DEBUG(("Reading data from socket %d", fd));
59
60   /* Check whether there is data available, without calling recv(). */
61   ioctlsocket(fd, FIONREAD, (unsigned long *)&argp);
62   if (argp == 0) {
63     /* Is this kludge or what? Without this thing this contraption
64        does not work at all!?. */
65     SleepEx(1, TRUE);
66     SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
67     silc_schedule_set_listen_fd(sock->schedule, sock->sock,
68                                 silc_schedule_get_fd_events(sock->schedule,
69                                                             sock->sock) |
70                                 SILC_TASK_READ, FALSE);
71     silc_set_errno(SILC_ERR_WOULD_BLOCK);
72     return -1;
73   }
74
75   /* Read the data from the socket. */
76   len = recv(fd, buf, buf_len, 0);
77   if (len == SOCKET_ERROR) {
78     len = WSAGetLastError();
79     silc_set_errno_posix(ret);
80     if (len == WSAEWOULDBLOCK || len == WSAEINTR) {
81       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
82       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
83                                   silc_schedule_get_fd_events(sock->schedule,
84                                                               sock->sock) |
85                                   SILC_TASK_READ, FALSE);
86       return -1;
87     }
88     SILC_LOG_DEBUG(("Cannot read from socket: %d", sock->sock));
89     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
90     return -2;
91   }
92
93   SILC_LOG_DEBUG(("Read %d bytes", len));
94
95   if (!len)
96     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
97
98   return len;
99 }
100
101 /* Stream write operation */
102
103 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
104                              SilcUInt32 data_len)
105 {
106   SilcSocketStream sock = stream;
107   SOCKET fd = sock->sock;
108   int ret;
109
110   SILC_LOG_DEBUG(("Writing data to socket %d", fd));
111
112   ret = send(fd, data, data_len,  0);
113   if (ret == SOCKET_ERROR) {
114     ret = WSAGetLastError();
115     silc_set_errno_posix(ret);
116     if (ret == WSAEWOULDBLOCK) {
117       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
118       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
119                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
120       return -1;
121     }
122     SILC_LOG_DEBUG(("Cannot write to socket"));
123     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
124     return -2;
125   }
126
127   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
128   if (silc_schedule_get_fd_events(sock->schedule, sock->sock) &
129       SILC_TASK_WRITE)
130     silc_schedule_set_listen_fd(sock->schedule, sock->sock,
131                                 SILC_TASK_READ, FALSE);
132
133   return ret;
134 }
135
136 /* Receive UDP packet.  QoS is not supported. */
137
138 int silc_socket_udp_stream_read(SilcStream stream, unsigned char *buf,
139                                 SilcUInt32 buf_len)
140 {
141   return silc_net_udp_receive(stream, NULL, 0, NULL, buf, buf_len);
142 }
143
144 /* Send UDP packet.  This always succeeds. */
145
146 int silc_socket_udp_stream_write(SilcStream stream, const unsigned char *data,
147                                  SilcUInt32 data_len)
148 {
149   SilcSocketStream sock = stream;
150
151   /* In connectionless state check if remote IP and port is provided */
152   if (!sock->connected && sock->ip && sock->port)
153     return silc_net_udp_send(stream, sock->ip, sock->port, data, data_len);
154
155   /* In connected state use normal writing to socket. */
156   return silc_socket_stream_write(stream, data, data_len);
157 }
158
159 /* Closes socket */
160
161 SilcBool silc_socket_stream_close(SilcStream stream)
162 {
163   SilcSocketStream socket_stream = stream;
164
165   if (socket_stream->schedule) {
166     silc_schedule_unset_listen_fd(socket_stream->schedule,
167                                   socket_stream->sock);
168     silc_schedule_task_del_by_fd(socket_stream->schedule,
169                                  socket_stream->sock);
170   }
171   silc_net_close_connection(socket_stream->sock);
172
173   return TRUE;
174 }
175
176 /* Destroys the stream */
177
178 void silc_socket_stream_destroy(SilcStream stream)
179 {
180   SilcSocketStream socket_stream = stream;
181
182   silc_socket_stream_close(socket_stream);
183   silc_free(socket_stream->ip);
184   silc_free(socket_stream->hostname);
185   if (socket_stream->schedule)
186     silc_schedule_task_del_by_fd(socket_stream->schedule, socket_stream->sock);
187
188   if (socket_stream->schedule)
189     silc_schedule_wakeup(socket_stream->schedule);
190
191   silc_free(socket_stream);
192 }
193
194 /* Sets stream notification callback for the stream */
195
196 SilcBool silc_socket_stream_notifier(SilcStream stream,
197                                      SilcSchedule schedule,
198                                      SilcStreamNotifier callback,
199                                      void *context)
200 {
201   SilcSocketStream socket_stream = stream;
202
203   SILC_LOG_DEBUG(("Setting stream notifier callback"));
204
205   socket_stream->notifier = callback;
206   socket_stream->notifier_context = context;
207   socket_stream->schedule = schedule;
208
209   if (socket_stream->notifier && socket_stream->schedule) {
210     /* Add the socket to scheduler.  Safe to call if already added. */
211     if (!silc_schedule_task_add_fd(socket_stream->schedule,
212                                    socket_stream->sock,
213                                    silc_socket_stream_io, socket_stream))
214       return FALSE;
215
216     /* Initially set socket for reading */
217     if (!silc_schedule_set_listen_fd(socket_stream->schedule,
218                                      socket_stream->sock,
219                                      SILC_TASK_READ, FALSE))
220       return FALSE;
221   } else if (socket_stream->schedule) {
222     /* Unschedule the socket */
223     silc_schedule_unset_listen_fd(socket_stream->schedule,
224                                   socket_stream->sock);
225     silc_schedule_task_del_by_fd(socket_stream->schedule,
226                                  socket_stream->sock);
227   }
228
229   if (socket_stream->schedule)
230     silc_schedule_wakeup(socket_stream->schedule);
231
232   return TRUE;
233 }