Decode JSON TRP messages, then send to main thread.
[trust_router.git] / trp / trps.c
1 #include <fcntl.h>
2 #include <event2/event.h>
3 #include <talloc.h>
4 #include <errno.h>
5 #include <unistd.h>
6
7 #include <gsscon.h>
8 #include <tr_rp.h>
9 #include <tr_event.h>
10 #include <tr_debug.h>
11 #include <trp_internal.h>
12
13
14 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
15 {
16   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
17   if (trps!=NULL)  {
18     trps->hostname=NULL;
19     trps->port=0;
20     trps->cookie=NULL;
21     trps->conn=NULL;
22     trps->mq=tr_mq_new(trps);
23     if (trps->mq==NULL) {
24       /* failed to allocate mq */
25       talloc_free(trps);
26       trps=NULL;
27     }
28   }
29   return trps;
30 }
31
32 void trps_free (TRPS_INSTANCE *trps)
33 {
34   if (trps!=NULL)
35     talloc_free(trps);
36 }
37
38 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
39 {
40   return tr_mq_pop(trps->mq);
41 }
42
43 void trps_mq_append(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
44 {
45   tr_mq_append(trps->mq, msg);
46 }
47
48 /* stand-in for a function that finds the connection for a particular peer */
49 #if 0
50 static TRP_CONNECTION *trps_find_connection(TRPS_INSTANCE *trps)
51 {
52   return trps->conn;
53 }
54 #endif
55
56 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
57 {
58   if (trps->conn==NULL)
59     trps->conn=new;
60   else
61     trp_connection_append(trps->conn, new);
62
63   talloc_steal(trps, new);
64 }
65
66 /* ok to call more than once; guarantees connection no longer in the list */
67 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
68 {
69   trps->conn=trp_connection_remove(trps->conn, remove);
70 }
71
72 int trps_send_msg (TRPS_INSTANCE *trps,
73                    int conn,
74                    gss_ctx_id_t gssctx,
75                    const char *msg_content)
76 {
77   int err=0;
78   int rc=0;
79
80   /* Send the request over the connection */
81   if (err = gsscon_write_encrypted_token (conn,
82                                           gssctx,
83                                           msg_content, 
84                                           strlen(msg_content))) {
85     tr_err( "trps_send_msg: Error sending message over connection.\n");
86     rc = -1;
87   }
88
89   return rc;
90 }
91
92 static int trps_listen (TRPS_INSTANCE *trps, int port) 
93 {
94     int rc = 0;
95     int conn = -1;
96     int optval = 1;
97
98     union {
99       struct sockaddr_storage storage;
100       struct sockaddr_in in4;
101     } addr;
102
103     struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
104
105     saddr->sin_port = htons (port);
106     saddr->sin_family = AF_INET;
107     saddr->sin_addr.s_addr = INADDR_ANY;
108
109     if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
110       return conn;
111
112     setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
113
114     if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
115       return rc;
116
117     if (0 > (rc = listen(conn, 512)))
118       return rc;
119
120     tr_debug("trps_listen: TRP Server listening on port %d", port);
121     return conn;
122 }
123
124 /* returns EACCES if authorization is denied */
125 int trps_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
126 {
127   TRPS_INSTANCE *inst = (TRPS_INSTANCE *)data;
128   int result=0;
129
130   if (0!=inst->auth_handler(clientName, displayName, inst->cookie)) {
131     tr_debug("trps_auth_cb: client '%.*s' denied authorization.", displayName->length, displayName->value);
132     result=EACCES; /* denied */
133   }
134
135   return result;
136 }
137
138 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
139 {
140   int err;
141   char *buf;
142   size_t buflen = 0;
143
144   tr_debug("trps_read_message: started");
145   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
146                                        *(trp_connection_get_gssctx(conn)), 
147                                         &buf,
148                                         &buflen)) {
149     tr_debug("trps_read_message: error");
150     if (buf)
151       free(buf);
152     return TRP_ERROR;
153   }
154
155   tr_debug("trps_read_message(): Request Received, %u bytes.", (unsigned) buflen);
156   tr_debug("trps_read_message(): %.*s", buflen, buf);
157
158   *msg=tr_msg_decode(buf, buflen);
159   free(buf);
160   return TRP_SUCCESS;
161 }
162
163 int trps_get_listener(TRPS_INSTANCE *trps,
164                       TRPS_MSG_FUNC msg_handler,
165                       TRP_AUTH_FUNC auth_handler,
166                       const char *hostname,
167                       unsigned int port,
168                       void *cookie)
169 {
170   int listen = -1;
171
172   if (0 > (listen = trps_listen(trps, port))) {
173     char errbuf[256];
174     if (0 == strerror_r(errno, errbuf, 256)) {
175       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
176     } else {
177       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
178     }
179   } 
180
181   if (listen > 0) {
182     /* opening port succeeded */
183     tr_debug("trps_get_listener: Opened port %d.", port);
184     
185     /* make this socket non-blocking */
186     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
187       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
188       close(listen);
189       listen=-1;
190     }
191   }
192
193   if (listen > 0) {
194     /* store the caller's request handler & cookie */
195     trps->msg_handler = msg_handler;
196     trps->auth_handler = auth_handler;
197     trps->hostname = talloc_strdup(trps, hostname);
198     trps->port = port;
199     trps->cookie = cookie;
200   }
201
202   return listen;
203 }
204
205 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
206 {
207   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
208   TR_MSG *msg=NULL;
209   TRP_RC rc=TRP_ERROR;
210
211   /* try to establish a GSS context */
212   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
213     tr_notice("tr_trps_conn_thread: failed to authorize connection");
214     pthread_exit(NULL);
215   }
216   tr_notice("trps_handle_connection: authorized connection");
217   
218   /* loop as long as the connection exists */
219   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
220     rc=trps_read_message(trps, conn, &msg);
221     switch(rc) {
222     case TRP_SUCCESS:
223       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
224       break;
225
226     case TRP_ERROR:
227       trp_connection_close(conn);
228       break;
229
230     default:
231       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
232     }
233   }
234
235   tr_debug("trps_handle_connection: connection closed.");
236   talloc_free(tmp_ctx);
237 }