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