No nethook available anymore.
[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   switch (cond)
115     {
116     case G_IO_IN:
117       event = HC_EVENT_READ;
118       r = recv (fd, &buffer, 1, MSG_PEEK);
119       if (r == 0)
120         event = HC_EVENT_CLOSE;
121       break;
122     case G_IO_HUP:
123       event = HC_EVENT_CLOSE;
124       break;
125     default:
126       /* TODO: handle other conditions and create error event */
127       g_warning ("Received an unexpected IO condition.");
128       break;
129     }
130   if (conn->func)
131     conn->func (conn, event, conn->data);
132   return TRUE;
133 }
134
135 void
136 hc_conn_set_driver_channel (HCConn *conn, int fd)
137 {
138   struct channel_layer *layer = g_slice_new (struct channel_layer);
139   layer->channel = g_io_channel_unix_new (fd);
140   conn->layer = layer;
141   conn->read = hc_conn_channel_read;
142   conn->write = hc_conn_channel_write;
143   conn->close = hc_conn_channel_close;
144   /* TODO: We must watch other events */
145   layer->watch = g_io_add_watch (layer->channel, G_IO_IN | G_IO_HUP,
146                                  hc_conn_watch, conn);
147   /* TODO: connection should be asynchronous so this could make sense */
148   if (conn->func)
149     conn->func (conn, HC_EVENT_CONNECT, conn->data);
150   fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
151 }
152
153
154 /* The core connection system */
155
156 HCConn *
157 hc_conn_new (HCClientFunc func, gpointer data)
158 {
159   HCConn *conn;
160   conn = g_slice_new (HCConn);
161   conn->func = func;
162   conn->data = data;
163   return conn;
164 }
165
166 void
167 hc_conn_set_callback (HCConn *conn, HCClientFunc func, gpointer data)
168 {
169   conn->func = func;
170   conn->data = data;
171 }
172
173 ssize_t
174 hc_conn_read (HCConn *conn, char *buffer, size_t len)
175 {
176   if (conn->read)
177     return conn->read (conn->layer, buffer, len);
178   return 0;
179 }
180
181 void
182 hc_conn_write (HCConn *conn, char *buffer, size_t len)
183 {
184   /* TODO: Do buffering or something like that */
185   /* Do we really need to? */
186   /* In case of error, we should do something */
187   if (conn->write)
188     conn->write (conn->layer, buffer, len);
189 }
190
191 void
192 hc_conn_close (HCConn *conn)
193 {
194   if (conn->close)
195     conn->close (conn->layer);
196   conn->read = NULL;
197   conn->write = NULL;
198   conn->close = NULL;
199   conn->func = NULL;
200   g_slice_free (HCConn, conn);
201 }