1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <errno.h>
21 #include <netdb.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <netinet/in.h>
28
29 #include <thrift/c_glib/thrift.h>
30 #include <thrift/c_glib/thrift_configuration.h>
31 #include <thrift/c_glib/transport/thrift_transport.h>
32 #include <thrift/c_glib/transport/thrift_socket.h>
33
34 /* object properties */
35 enum _ThriftSocketProperties
36 {
37 PROP_0,
38 PROP_THRIFT_SOCKET_HOSTNAME,
39 PROP_THRIFT_SOCKET_PORT,
40 PROP_THRIFT_SOCKET_PATH,
41 PROP_THRIFT_SOCKET_CONFIGURATION,
42 PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE,
43 PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE
44 };
45
G_DEFINE_TYPE(ThriftSocket,thrift_socket,THRIFT_TYPE_TRANSPORT)46 G_DEFINE_TYPE(ThriftSocket, thrift_socket, THRIFT_TYPE_TRANSPORT)
47
48 /* implements thrift_transport_is_open */
49 gboolean
50 thrift_socket_is_open (ThriftTransport *transport)
51 {
52 ThriftSocket *socket = THRIFT_SOCKET (transport);
53 return socket->sd != THRIFT_INVALID_SOCKET;
54 }
55
56 /* overrides thrift_transport_peek */
57 gboolean
thrift_socket_peek(ThriftTransport * transport,GError ** error)58 thrift_socket_peek (ThriftTransport *transport, GError **error)
59 {
60 gboolean result = FALSE;
61 guint8 buf;
62 int r;
63 int errno_copy;
64
65 ThriftSocket *socket = THRIFT_SOCKET (transport);
66
67 if (thrift_socket_is_open (transport))
68 {
69 r = recv (socket->sd, &buf, 1, MSG_PEEK);
70 if (r == -1)
71 {
72 errno_copy = errno;
73
74 #if defined __FreeBSD__ || defined __MACH__
75 /* FreeBSD returns -1 and ECONNRESET if the socket was closed by the other
76 side */
77 if (errno_copy == ECONNRESET)
78 {
79 thrift_socket_close (transport, error);
80 }
81 else
82 {
83 #endif
84
85 g_set_error (error,
86 THRIFT_TRANSPORT_ERROR,
87 THRIFT_TRANSPORT_ERROR_SOCKET,
88 "failed to peek at socket - %s",
89 strerror (errno_copy));
90
91 #if defined __FreeBSD__ || defined __MACH__
92 }
93 #endif
94 }
95 else if (r > 0)
96 {
97 result = TRUE;
98 }
99 }
100
101 return result;
102 }
103
104
105 /* implements thrift_transport_close */
106 gboolean
thrift_socket_close(ThriftTransport * transport,GError ** error)107 thrift_socket_close (ThriftTransport *transport, GError **error)
108 {
109 ThriftSocket *socket = THRIFT_SOCKET (transport);
110
111 if (close (socket->sd) == -1)
112 {
113 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CLOSE,
114 "unable to close socket - %s",
115 strerror(errno));
116 return FALSE;
117 }
118
119 socket->sd = THRIFT_INVALID_SOCKET;
120 return TRUE;
121 }
122
123 /* implements thrift_transport_open */
124 gboolean
thrift_socket_open(ThriftTransport * transport,GError ** error)125 thrift_socket_open (ThriftTransport *transport, GError **error)
126 {
127 struct hostent *hp = NULL;
128 struct sockaddr_in pin;
129 int err;
130 int errno_copy;
131 #if defined(HAVE_GETHOSTBYNAME_R)
132 struct hostent he;
133 char buf[1024];
134 #endif
135
136 ThriftSocket *tsocket = THRIFT_SOCKET (transport);
137 g_return_val_if_fail (tsocket->sd == THRIFT_INVALID_SOCKET, FALSE);
138
139 if (tsocket->path) {
140 /* create a socket structure */
141 struct sockaddr_un pin;
142 memset (&pin, 0, sizeof(pin));
143 pin.sun_family = AF_UNIX;
144 memcpy(pin.sun_path, tsocket->path, strlen(tsocket->path) + 1);
145
146 /* create the socket */
147 if ((tsocket->sd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1)
148 {
149 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
150 "failed to create socket for path %s: - %s",
151 tsocket->path,
152 strerror(errno));
153 return FALSE;
154 }
155
156 /* open a connection */
157 if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
158 {
159 errno_copy = errno;
160 thrift_socket_close(transport, NULL);
161 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
162 "failed to connect to path %s: - %s",
163 tsocket->path, strerror(errno_copy));
164 return FALSE;
165 }
166 return TRUE;
167 }
168
169 /* lookup the destination host */
170 #if defined(HAVE_GETHOSTBYNAME_R)
171 if (gethostbyname_r (tsocket->hostname, &he, buf, 1024, &hp, &err) != 0 || hp == NULL)
172 #else
173 if ((hp = gethostbyname (tsocket->hostname)) == NULL && (err = h_errno))
174 #endif
175 {
176 /* host lookup failed, bail out with an error */
177 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_HOST,
178 "host lookup failed for %s:%d - %s",
179 tsocket->hostname, tsocket->port,
180 hstrerror (err));
181 return FALSE;
182 }
183
184 /* create a socket structure */
185 memset (&pin, 0, sizeof(pin));
186 pin.sin_family = AF_INET;
187 pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr_list[0]))->s_addr;
188 pin.sin_port = htons (tsocket->port);
189
190 /* create the socket */
191 if ((tsocket->sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
192 {
193 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_SOCKET,
194 "failed to create socket for host %s:%d - %s",
195 tsocket->hostname, tsocket->port,
196 strerror(errno));
197 return FALSE;
198 }
199
200 /* open a connection */
201 if (connect (tsocket->sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
202 {
203 errno_copy = errno;
204 thrift_socket_close(transport, NULL);
205 g_set_error (error, THRIFT_TRANSPORT_ERROR, THRIFT_TRANSPORT_ERROR_CONNECT,
206 "failed to connect to host %s:%d - %s",
207 tsocket->hostname, tsocket->port, strerror(errno_copy));
208 return FALSE;
209 }
210
211 return TRUE;
212 }
213
214
215 /* implements thrift_transport_read */
216 gint32
thrift_socket_read(ThriftTransport * transport,gpointer buf,guint32 len,GError ** error)217 thrift_socket_read (ThriftTransport *transport, gpointer buf,
218 guint32 len, GError **error)
219 {
220 gint ret = 0;
221 guint got = 0;
222
223 ThriftSocket *socket = THRIFT_SOCKET (transport);
224 ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (transport);
225 if(!ttc->checkReadBytesAvailable (transport, len, error))
226 {
227 return -1;
228 }
229
230 while (got < len)
231 {
232 ret = recv (socket->sd, (guint8 *)buf + got, len-got, 0);
233 if (ret <= 0)
234 {
235 g_set_error (error, THRIFT_TRANSPORT_ERROR,
236 THRIFT_TRANSPORT_ERROR_RECEIVE,
237 "failed to read %d bytes - %s", len, strerror(errno));
238 return -1;
239 }
240 got += ret;
241 }
242
243 return got;
244 }
245
246 /* implements thrift_transport_read_end
247 * called when write is complete. nothing to do on our end. */
248 gboolean
thrift_socket_read_end(ThriftTransport * transport,GError ** error)249 thrift_socket_read_end (ThriftTransport *transport, GError **error)
250 {
251 /* satisfy -Wall */
252 THRIFT_UNUSED_VAR (transport);
253 THRIFT_UNUSED_VAR (error);
254 return TRUE;
255 }
256
257 /* implements thrift_transport_write */
258 gboolean
thrift_socket_write(ThriftTransport * transport,const gpointer buf,const guint32 len,GError ** error)259 thrift_socket_write (ThriftTransport *transport, const gpointer buf,
260 const guint32 len, GError **error)
261 {
262 gint ret = 0;
263 guint sent = 0;
264
265 ThriftSocket *socket = THRIFT_SOCKET (transport);
266 g_return_val_if_fail (socket->sd != THRIFT_INVALID_SOCKET, FALSE);
267
268 while (sent < len)
269 {
270 ret = send (socket->sd, (guint8 *)buf + sent, len - sent, 0);
271 if (ret < 0)
272 {
273 g_set_error (error, THRIFT_TRANSPORT_ERROR,
274 THRIFT_TRANSPORT_ERROR_SEND,
275 "failed to send %d bytes - %s", len, strerror(errno));
276 return FALSE;
277 }
278 sent += ret;
279 }
280
281 return TRUE;
282 }
283
284 /* implements thrift_transport_write_end
285 * called when write is complete. nothing to do on our end. */
286 gboolean
thrift_socket_write_end(ThriftTransport * transport,GError ** error)287 thrift_socket_write_end (ThriftTransport *transport, GError **error)
288 {
289 /* satisfy -Wall */
290 THRIFT_UNUSED_VAR (transport);
291 THRIFT_UNUSED_VAR (error);
292 return TRUE;
293 }
294
295 /* implements thrift_transport_flush
296 * flush pending data. since we are not buffered, this is a no-op */
297 gboolean
thrift_socket_flush(ThriftTransport * transport,GError ** error)298 thrift_socket_flush (ThriftTransport *transport, GError **error)
299 {
300 /* satisfy -Wall */
301 THRIFT_UNUSED_VAR (transport);
302 THRIFT_UNUSED_VAR (error);
303 return TRUE;
304 }
305
306 /* initializes the instance */
307 static void
thrift_socket_init(ThriftSocket * socket)308 thrift_socket_init (ThriftSocket *socket)
309 {
310 socket->sd = THRIFT_INVALID_SOCKET;
311 }
312
313 /* destructor */
314 static void
thrift_socket_finalize(GObject * object)315 thrift_socket_finalize (GObject *object)
316 {
317 ThriftSocket *socket = THRIFT_SOCKET (object);
318
319 if (socket->hostname != NULL)
320 {
321 g_free (socket->hostname);
322 }
323 socket->hostname = NULL;
324 if (socket->path != NULL)
325 {
326 g_free (socket->path);
327 }
328
329 if (socket->sd != THRIFT_INVALID_SOCKET)
330 {
331 close (socket->sd);
332 }
333 socket->sd = THRIFT_INVALID_SOCKET;
334 }
335
336 /* property accessor */
337 void
thrift_socket_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)338 thrift_socket_get_property (GObject *object, guint property_id,
339 GValue *value, GParamSpec *pspec)
340 {
341 ThriftSocket *socket = THRIFT_SOCKET (object);
342 ThriftTransport *tt = THRIFT_TRANSPORT (object);
343
344 THRIFT_UNUSED_VAR (pspec);
345
346 switch (property_id)
347 {
348 case PROP_THRIFT_SOCKET_HOSTNAME:
349 g_value_set_string (value, socket->hostname);
350 break;
351 case PROP_THRIFT_SOCKET_PORT:
352 g_value_set_uint (value, socket->port);
353 break;
354 case PROP_THRIFT_SOCKET_PATH:
355 g_value_set_string (value, socket->path);
356 break;
357 case PROP_THRIFT_SOCKET_CONFIGURATION:
358 g_value_set_object (value, tt->configuration);
359 break;
360 case PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE:
361 g_value_set_long (value, tt->remainingMessageSize_);
362 break;
363 case PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE:
364 g_value_set_long (value, tt->knowMessageSize_);
365 break;
366 }
367 }
368
369 /* property mutator */
370 void
thrift_socket_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)371 thrift_socket_set_property (GObject *object, guint property_id,
372 const GValue *value, GParamSpec *pspec)
373 {
374 ThriftSocket *socket = THRIFT_SOCKET (object);
375 ThriftTransport *tt = THRIFT_TRANSPORT (object);
376
377 THRIFT_UNUSED_VAR (pspec);
378
379 switch (property_id)
380 {
381 case PROP_THRIFT_SOCKET_HOSTNAME:
382 if (socket->hostname) {
383 g_free(socket->hostname);
384 }
385 socket->hostname = g_strdup (g_value_get_string (value));
386 break;
387 case PROP_THRIFT_SOCKET_PORT:
388 socket->port = g_value_get_uint (value);
389 break;
390 case PROP_THRIFT_SOCKET_PATH:
391 if (socket->path) {
392 g_free(socket->path);
393 }
394 socket->path = g_strdup (g_value_get_string (value));
395 break;
396 case PROP_THRIFT_SOCKET_CONFIGURATION:
397 tt->configuration = g_value_dup_object (value);
398 break;
399 case PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE:
400 tt->remainingMessageSize_ = g_value_get_long (value);
401 break;
402 case PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE:
403 tt->knowMessageSize_ = g_value_get_long (value);
404 }
405 }
406
407 /* initializes the class */
408 static void
thrift_socket_class_init(ThriftSocketClass * cls)409 thrift_socket_class_init (ThriftSocketClass *cls)
410 {
411 ThriftTransportClass *ttc = THRIFT_TRANSPORT_CLASS (cls);
412 GObjectClass *gobject_class = G_OBJECT_CLASS (cls);
413 GParamSpec *param_spec = NULL;
414
415 /* setup accessors and mutators */
416 gobject_class->get_property = thrift_socket_get_property;
417 gobject_class->set_property = thrift_socket_set_property;
418
419 param_spec = g_param_spec_string ("hostname",
420 "hostname (construct)",
421 "Set the hostname of the remote host",
422 "localhost", /* default value */
423 G_PARAM_CONSTRUCT_ONLY |
424 G_PARAM_READWRITE);
425 g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_HOSTNAME,
426 param_spec);
427
428 param_spec = g_param_spec_uint ("port",
429 "port (construct)",
430 "Set the port of the remote host",
431 0u, /* min */
432 65535u, /* max */
433 9090, /* default by convention */
434 G_PARAM_CONSTRUCT_ONLY |
435 G_PARAM_READWRITE);
436 g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PORT,
437 param_spec);
438
439 param_spec = g_param_spec_string ("path",
440 "path (construct)",
441 "Set the path of the remote host",
442 NULL, /* default value */
443 G_PARAM_CONSTRUCT_ONLY |
444 G_PARAM_READWRITE);
445 g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_PATH,
446 param_spec);
447
448 param_spec = g_param_spec_object ("configuration",
449 "configuration",
450 "Thrift Configuration",
451 THRIFT_TYPE_CONFIGURATION,
452 G_PARAM_CONSTRUCT_ONLY |
453 G_PARAM_READWRITE);
454 g_object_class_install_property (gobject_class, PROP_THRIFT_SOCKET_CONFIGURATION,
455 param_spec);
456
457 param_spec = g_param_spec_long ("remainingmessagesize",
458 "remainingmessagesize (construct)",
459 "Set the remaining message size",
460 0, /* min */
461 G_MAXINT32, /* max */
462 DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */
463 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
464 g_object_class_install_property (gobject_class,
465 PROP_THRIFT_SOCKET_REMAINING_MESSAGE_SIZE,
466 param_spec);
467
468 param_spec = g_param_spec_long ("knowmessagesize",
469 "knowmessagesize (construct)",
470 "Set the known size of the message",
471 0, /* min */
472 G_MAXINT32, /* max */
473 DEFAULT_MAX_MESSAGE_SIZE, /* default by construct */
474 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
475 g_object_class_install_property (gobject_class,
476 PROP_THRIFT_SOCKET_KNOW_MESSAGE_SIZE,
477 param_spec);
478
479 gobject_class->finalize = thrift_socket_finalize;
480 ttc->is_open = thrift_socket_is_open;
481 ttc->peek = thrift_socket_peek;
482 ttc->open = thrift_socket_open;
483 ttc->close = thrift_socket_close;
484 ttc->read = thrift_socket_read;
485 ttc->read_end = thrift_socket_read_end;
486 ttc->write = thrift_socket_write;
487 ttc->write_end = thrift_socket_write_end;
488 ttc->flush = thrift_socket_flush;
489 }
490