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