Accept GNUTLS priority in configuration file.
[cascardo/rnetproxy.git] / hcconn.c
1 /*
2  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include "hcconn.h"
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/socket.h>
24 #include "hcconn_internal.h"
25
26 /* The server connection watch */
27
28 struct hc_server_cb
29 {
30   GIOChannel *channel;
31   HCServerFunc func;
32   gpointer data;
33 };
34
35 static void
36 hc_server_cb_destroy (gpointer cb)
37 {
38   g_slice_free (struct hc_server_cb, cb);
39 }
40
41 static gboolean
42 hc_server_watch (GIOChannel *channel, GIOCondition cond, gpointer data)
43 {
44   struct hc_server_cb *cb = data;
45   int fd = g_io_channel_unix_get_fd (channel);
46   struct sockaddr addr;
47   socklen_t saddr = sizeof (addr);
48   int client = accept (fd, &addr, &saddr);
49   if (client >= 0 && cb->func)
50     cb->func (client, &addr, saddr, cb->data);
51   return TRUE;
52 }
53
54 void
55 hc_server_add_watch (int fd,
56                      HCServerFunc func,
57                      gpointer data)
58 {
59   struct hc_server_cb *cb;
60   cb = g_slice_new (struct hc_server_cb);
61   cb->channel = g_io_channel_unix_new (fd);
62   cb->func = func;
63   cb->data = data;
64   /* TODO: we should have some way to remove this watch */
65   g_io_add_watch_full (cb->channel, G_PRIORITY_DEFAULT, G_IO_IN,
66                        hc_server_watch, cb, hc_server_cb_destroy);
67 }
68
69
70 /* The IOChannel (simple socket) layer */
71
72 struct channel_layer
73 {
74   GIOChannel *channel;
75   guint watch;
76 };
77
78 ssize_t
79 hc_conn_channel_read (gpointer data, char *buffer, size_t len)
80 {
81   struct channel_layer *layer = data;
82   int fd = g_io_channel_unix_get_fd (layer->channel);
83   return read (fd, buffer, len);
84 }
85
86 ssize_t
87 hc_conn_channel_write (gpointer data, char *buffer, size_t len)
88 {
89   struct channel_layer *layer = data;
90   int fd = g_io_channel_unix_get_fd (layer->channel);
91   return write (fd, buffer, len);
92 }
93
94 void
95 hc_conn_channel_close (gpointer data)
96 {
97   struct channel_layer *layer = data;
98   int fd = g_io_channel_unix_get_fd (layer->channel);
99   g_source_remove (layer->watch);
100   close (fd);
101   g_io_channel_unref (layer->channel);
102   g_slice_free (struct channel_layer, layer);
103 }
104
105 gboolean
106 hc_conn_watch (GIOChannel *channel, GIOCondition cond, gpointer data)
107 {
108   HCConn *conn = data;
109   /* TODO: What about other events, like closing? */
110   HCEvent event;
111   int fd = g_io_channel_unix_get_fd (channel);
112   char buffer;
113   int r;
114   if (cond & G_IO_IN)
115     {
116       event = HC_EVENT_READ;
117       r = recv (fd, &buffer, 1, MSG_PEEK);
118       if (r == 0)
119         {
120           event = HC_EVENT_CLOSE;
121         }
122       else if (r == -1)
123         {
124           /* FIXME: create HC_EVENT_ERROR */
125           event = HC_EVENT_CLOSE;
126         }
127     }
128   else if (cond & G_IO_HUP)
129     {
130       event = HC_EVENT_CLOSE;
131     }
132   else if (cond & G_IO_ERR)
133     {
134       /* FIXME: create HC_EVENT_ERROR */
135       event = HC_EVENT_CLOSE;
136     }
137   else
138     {
139       /* TODO: handle other conditions and create error event */
140       g_warning ("Received an unexpected IO condition.");
141     }
142   if (conn->func)
143     conn->func (conn, event, conn->data);
144   return TRUE;
145 }
146
147 int
148 hc_conn_set_driver_channel (HCConn *conn, int fd)
149 {
150   struct channel_layer *layer = g_slice_new (struct channel_layer);
151   layer->channel = g_io_channel_unix_new (fd);
152   conn->layer = layer;
153   conn->read = hc_conn_channel_read;
154   conn->write = hc_conn_channel_write;
155   conn->close = hc_conn_channel_close;
156   /* TODO: We must watch other events */
157   layer->watch = g_io_add_watch (layer->channel,
158                                  G_IO_IN | G_IO_HUP | G_IO_ERR,
159                                  hc_conn_watch, conn);
160   /* TODO: connection should be asynchronous so this could make sense */
161   if (conn->func)
162     conn->func (conn, HC_EVENT_CONNECT, conn->data);
163   fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
164   return 0;
165 }
166
167
168 /* The core connection system */
169
170 HCConn *
171 hc_conn_new (HCClientFunc func, gpointer data)
172 {
173   HCConn *conn;
174   conn = g_slice_new (HCConn);
175   conn->func = func;
176   conn->data = data;
177   return conn;
178 }
179
180 void
181 hc_conn_set_callback (HCConn *conn, HCClientFunc func, gpointer data)
182 {
183   conn->func = func;
184   conn->data = data;
185 }
186
187 ssize_t
188 hc_conn_read (HCConn *conn, char *buffer, size_t len)
189 {
190   if (conn->read)
191     return conn->read (conn->layer, buffer, len);
192   return 0;
193 }
194
195 void
196 hc_conn_write (HCConn *conn, char *buffer, size_t len)
197 {
198   /* TODO: Do buffering or something like that */
199   /* Do we really need to? */
200   /* In case of error, we should do something */
201   if (conn->write)
202     conn->write (conn->layer, buffer, len);
203 }
204
205 void
206 hc_conn_close (HCConn *conn)
207 {
208   if (conn->close)
209     conn->close (conn->layer);
210   conn->read = NULL;
211   conn->write = NULL;
212   conn->close = NULL;
213   conn->func = NULL;
214   g_slice_free (HCConn, conn);
215 }