Added implementation of UDP connection
[apps/native/gear-racing-car.git] / src / udp_connection.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Flora License, Version 1.1 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "udp_connection.h"
18 #include <gio/gio.h>
19 #include <stdlib.h>
20 #include "log.h"
21 #define MESSAGE_IN_BUF_SIZE 512
22
23 struct udp_connection {
24         GSocket *socket;
25         udp_receive_cb receive_cb;
26         GIOChannel *channel;
27         guint watch_id;
28         GError *error;
29 };
30
31 static gboolean _channel_ready_cb(GIOChannel *source, GIOCondition cond, gpointer data);
32 static void _connection_release_resources(udp_connection_t *connection);
33
34 udp_connection_t *udp_connection_create(int port)
35 {
36         udp_connection_t *connection = (udp_connection_t*) calloc(sizeof(udp_connection_t), 1);
37         if(connection == NULL) {
38                 _E("Failed to calloc allocate memory");
39                 return NULL;
40         }
41
42         connection->socket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &(connection->error));
43         if(!connection->socket) {
44                 _E("Failed to create socket");
45                 _connection_release_resources(connection);
46                 return NULL;
47         }
48         _D("Socket created");
49
50         GInetAddress *inet_addr = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
51         if(!inet_addr) {
52                 _E("Failed to obtain inet any address");
53                 _connection_release_resources(connection);
54                 return NULL;
55         }
56         gchar *address_str = g_inet_address_to_string(inet_addr);
57         _D("Obtained address: %s", address_str);
58
59         GInetSocketAddress *socket_addr = (GInetSocketAddress*) g_inet_socket_address_new(inet_addr, port);
60         g_object_unref(inet_addr);
61         if(!socket_addr) {
62                 _E("Failed to get socket address");
63                 g_free(address_str);
64                 _connection_release_resources(connection);
65                 return NULL;
66         }
67
68         gboolean res = g_socket_bind(connection->socket, (GSocketAddress*) socket_addr, TRUE, &(connection->error));
69         g_object_unref(socket_addr);
70         if(!res) {
71                 _E("Failed to bind socket: %s", connection->error->message);
72                 g_free(address_str);
73                 _connection_release_resources(connection);
74                 return NULL;
75         }
76         _D("Bound socket to %s:%d", address_str, port);
77         g_socket_set_blocking(connection->socket, FALSE);
78
79         int socket_fd = g_socket_get_fd(connection->socket);
80         connection->channel = g_io_channel_unix_new(socket_fd);
81         connection->watch_id = g_io_add_watch(connection->channel, G_IO_IN, _channel_ready_cb, connection);
82         g_io_channel_unref(connection->channel);
83
84         g_free(address_str);
85         return connection;
86 }
87
88 int udp_connection_send(udp_connection_t *connection, const char *data, unsigned short int size, const char *address, int port)
89 {
90         GIOCondition cond = g_socket_condition_check(connection->socket, G_IO_OUT);
91         if(cond != G_IO_OUT) {
92                 _E("Failed to send data - socket is not G_IO_OUT");
93                 return -1;
94         }
95
96         GInetSocketAddress *receiver_address = (GInetSocketAddress*) g_inet_socket_address_new_from_string(address, port);
97         if(!receiver_address) {
98                 _E("Failed to obtain socket address from %s:%d", address, port);
99                 return -1;
100         }
101
102         GError *error = NULL;
103         gssize wr_size = g_socket_send_to(connection->socket, (GSocketAddress*) receiver_address, data, size, NULL, &error);
104         if(wr_size != size) {
105                 _E("Error sending data to %s:%d - sent only %d", address, port, wr_size);
106                 g_object_unref(receiver_address);
107                 g_error_free(error);
108                 return -1;
109         }
110         g_object_unref(receiver_address);
111         _D("Sent %d bytes", size);
112         return 0;
113 }
114
115 void udp_connection_set_receive_cb(udp_connection_t *connection, udp_receive_cb callback)
116 {
117         connection->receive_cb = callback;
118 }
119
120 void udp_connection_destroy(udp_connection_t *connection)
121 {
122         _connection_release_resources(connection);
123 }
124
125 static gboolean _channel_ready_cb(GIOChannel *source, GIOCondition cond, gpointer data)
126 {
127         udp_connection_t *connection = (udp_connection_t*) data;
128         GError *error = NULL;
129         GInetSocketAddress *socket_address = NULL;
130         gchar buffer[MESSAGE_IN_BUF_SIZE];
131
132         gssize size = g_socket_receive_from(connection->socket, (GSocketAddress**) &socket_address, buffer, MESSAGE_IN_BUF_SIZE, NULL, &error);
133         if(size < 0) {
134                 _E("Cannot read data from socket");
135                 g_error_free(error);
136                 return FALSE;
137         }
138
139         if(size == MESSAGE_IN_BUF_SIZE) {
140                 _W("Packet dropped due to its size");
141                 g_error_free(error);
142                 return TRUE;
143         }
144
145         if(size == 0) {
146                 _W("No data to read");
147                 return TRUE;
148         }
149
150         GInetAddress *address= g_inet_socket_address_get_address(socket_address);
151         if(!address) {
152                 _E("Failed to obtain the address of received message");
153                 return TRUE;
154         }
155
156         gchar *address_str = g_inet_address_to_string(address);
157         if(!address_str) {
158                 _E("Failed to obtain the address in text of received message");
159                 g_object_unref(address);
160                 return TRUE;
161         }
162
163         int port = g_inet_socket_address_get_port(socket_address);
164         if(connection->receive_cb) {
165                 connection->receive_cb(buffer, size, address_str, port);
166         }
167
168         g_free(address_str);
169         g_object_unref(socket_address);
170
171         return TRUE;
172 }
173
174 static void _connection_release_resources(udp_connection_t *connection)
175 {
176         if(!connection) {
177                 return;
178         }
179
180         if(connection->error) {
181                 g_error_free(connection->error);
182         }
183
184         if(connection->socket) {
185                 g_socket_close(connection->socket, NULL);
186         }
187
188         free(connection);
189 }