Added SILC Server library.
[silc.git] / lib / silcutil / unix / silcunixsocketstream.c
1 /*
2
3   silcunixsocketstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2005 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 /* $Id$ */
20
21 #include "silc.h"
22
23 /* QoS read handler, this will call the read and write events to indicate
24    that data is available again after a timeout. */
25
26 SILC_TASK_CALLBACK(silc_socket_read_qos)
27 {
28   SilcSocketQos qos = context;
29   qos->applied = TRUE;
30   silc_schedule_set_listen_fd(qos->sock->schedule, qos->sock->sock,
31                               (SILC_TASK_READ | SILC_TASK_WRITE), TRUE);
32   qos->applied = FALSE;
33   silc_schedule_set_listen_fd(qos->sock->schedule, qos->sock->sock,
34                               SILC_TASK_READ, FALSE);
35 }
36
37 /* Stream read operation */
38
39 int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
40                             SilcUInt32 buf_len)
41 {
42   SilcSocketStream sock = stream;
43   int len = 0;
44   struct timeval curtime;
45   unsigned char *qosbuf;
46
47   SILC_LOG_DEBUG(("Reading data from socket %d", sock->sock));
48
49   /* Handle the simple non-QoS reading. */
50   if (!sock->qos) {
51     len = read(sock->sock, buf, buf_len);
52     if (len < 0) {
53       if (errno == EAGAIN || errno == EINTR) {
54         SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
55         silc_schedule_set_listen_fd(sock->schedule, sock->sock,
56                                     SILC_TASK_READ, FALSE);
57         return -1;
58       }
59       SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
60                       sock->sock, strerror(errno)));
61       silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
62       sock->sock_error = errno;
63       return -2;
64     }
65
66     SILC_LOG_DEBUG(("Read %d bytes", len));
67
68     if (!len)
69       silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
70
71     return len;
72   }
73
74   /* We have QoS set, and reading is done via the QoS system. */
75   qosbuf = sock->qos->buffer;
76
77   /* If QoS was applied, return the data that was pending. */
78   if (sock->qos->applied && sock->qos->data_len) {
79     memcpy(buf, qosbuf, sock->qos->data_len);
80     sock->qos->data_len = 0;
81     return sock->qos->data_len;
82   }
83
84   /* If we have active QoS data pending, return with no data */
85   if (sock->qos->data_len) {
86     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
87     return -1;
88   }
89
90   /* Read the data from the socket.  Never read more than the max limit. */
91   len = (buf_len < sock->qos->read_limit_bytes ? buf_len :
92          sock->qos->read_limit_bytes);
93   len = read(sock->sock, qosbuf, len);
94   if (len < 0) {
95     if (errno == EAGAIN || errno == EINTR) {
96       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
97       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
98                                   SILC_TASK_READ, FALSE);
99       return -1;
100     }
101     SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
102                     sock->sock, strerror(errno)));
103     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
104     silc_schedule_task_del_by_context(sock->schedule, sock->qos);
105     sock->qos->data_len = 0;
106     sock->sock_error = errno;
107     return -2;
108   }
109
110   SILC_LOG_DEBUG(("Read %d bytes", len));
111
112   if (!len) {
113     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
114     silc_schedule_task_del_by_context(sock->schedule, sock->qos);
115     sock->qos->data_len = 0;
116     return 0;
117   }
118
119   /* If we have passed the rate time limit, set our new time limit,
120      and zero the rate limit.  This limits reads per second. */
121   silc_gettimeofday(&curtime);
122   if (!silc_compare_timeval(&curtime, &sock->qos->next_limit)) {
123     curtime.tv_sec++;
124     sock->qos->next_limit = curtime;
125     sock->qos->cur_rate = 0;
126   }
127   sock->qos->cur_rate++;
128
129   /* If we are not within rate limit apply QoS for the read data */
130   if (sock->qos->cur_rate > sock->qos->read_rate) {
131     silc_schedule_task_add_timeout(sock->schedule, silc_socket_read_qos,
132                                    sock->qos, sock->qos->limit_sec,
133                                    sock->qos->limit_usec);
134     sock->qos->data_len = len;
135
136     /* Rate limit kicked in, do not return data yet */
137     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
138     return -1;
139   }
140
141   /* Return the data from the QoS buffer */
142   memcpy(buf, qosbuf, len);
143   return len;
144 }
145
146 /* Stream write operation */
147
148 int silc_socket_stream_write(SilcStream stream, const unsigned char *data,
149                              SilcUInt32 data_len)
150 {
151   SilcSocketStream sock = stream;
152   int ret;
153
154   SILC_LOG_DEBUG(("Writing data to socket %d", sock->sock));
155
156   ret = write(sock->sock, data, data_len);
157   if (ret < 0) {
158     if (errno == EAGAIN || errno == EINTR) {
159       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
160       silc_schedule_set_listen_fd(sock->schedule, sock->sock,
161                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
162       return -1;
163     }
164     SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
165     silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
166     sock->sock_error = errno;
167     return -2;
168   }
169
170   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
171   silc_schedule_set_listen_fd(sock->schedule, sock->sock,
172                               SILC_TASK_READ, FALSE);
173
174   return ret;
175 }
176
177 #if 0
178 /* Returns human readable socket error message */
179
180 SilcBool silc_socket_get_error(SilcStream sock, char *error,
181                            SilcUInt32 error_len)
182 {
183   char *err;
184
185   if (!sock->sock_error)
186     return FALSE;
187
188   err = strerror(sock->sock_error);
189   if (strlen(err) > error_len)
190     return FALSE;
191
192   memset(error, 0, error_len);
193   memcpy(error, err, strlen(err));
194   return TRUE;
195 }
196 #endif /* 0 */