1 /* $Id: proto_push_method_query.c 20800 2012-01-19 05:13:45Z m-oki $ */
2
3 /*
4 * Copyright (c) 2012, Internet Initiative Japan, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31
32 #include <inttypes.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <axp_extern.h>
37 #include <arms_xml_tag.h>
38 #include <libarms_log.h>
39
40 #include <libarms/malloc.h>
41 #include <transaction/transaction.h>
42 #include <protocol/arms_methods.h>
43
44 /*
45 * push method query - new ARMS protocol method w/ SMF-NAT
46 */
47 static int
48 store_method_query(AXP *, int, int, int, const char *, size_t, void *);
49
50 static void *method_query_context(tr_ctx_t *);
51 static void method_query_release(tr_ctx_t *);
52 static int method_query_request(transaction *, char *, int, int *);
53 static int method_query_parse(transaction *, const char *, int);
54 static int method_query_judgement(transaction *, AXP *);
55
56 arms_method_t method_query_methods = {
57 ARMS_TR_METHOD_QUERY, /* pm_type */
58 "push-method-query", /* pm_string */
59 NULL, /* pm_schema */
60 0, /* pm_flags */
61 NULL, /* pm_response */
62 method_query_request, /* pm_done */
63 NULL, /* pm_exec */
64 NULL, /* pm_copyarg */
65 NULL, /* rollback */
66 method_query_context, /* pm_context */
67 method_query_release, /* pm_release */
68 method_query_parse, /* pm_parse */
69 };
70
71 static struct axp_schema method_query_method[] = {
72 {ARMS_TAG_PUSH_METHOD, "push-method",
73 AXP_TYPE_TEXT, NULL, store_method_query, NULL},
74 {ARMS_TAG_TUNNEL_URL, "https-tunnel-url",
75 AXP_TYPE_TEXT, NULL, store_method_query, NULL},
76 {ARMS_TAG_ECHO_INTERVAL, "https-tunnel-echo-interval",
77 AXP_TYPE_INT, NULL, store_method_query, NULL},
78
79 {0, NULL, 0, NULL, NULL, NULL}
80 };
81
82 static struct axp_schema method_query_res[] = {
83 {ARMS_TAG_RCODE, "result-code",
84 AXP_TYPE_INT, NULL, NULL, NULL},
85 {ARMS_TAG_RDESC, "description",
86 AXP_TYPE_TEXT, NULL, NULL, NULL},
87 {ARMS_TAG_METHOD_QUERY_RES, "push-method-query-response",
88 AXP_TYPE_CHILD, NULL, NULL, method_query_method},
89
90 {0, NULL, 0, NULL, NULL, NULL}
91 };
92
93 static char *method_query_res_attr[] = {
94 "type", NULL,
95 NULL, NULL
96 };
97
98 static struct axp_schema method_query_res_body[] = {
99 {ARMS_TAG_RES, "arms-response", AXP_TYPE_CHILD,
100 method_query_res_attr, NULL,
101 method_query_res},
102
103 {0, NULL, 0, NULL, NULL, NULL}
104 };
105
106 static struct axp_schema method_query_res_msg[] = {
107 {ARMS_TAG_MSG, "arms-message", AXP_TYPE_CHILD,
108 NULL, store_method_query, method_query_res_body},
109
110 {0, NULL, 0, NULL, NULL, NULL}
111 };
112
113 struct method_query_arg {
114 AXP *axp;
115 /* method priority list */
116 int nmethods;
117 int method_info[MAX_METHOD_INFO];
118 int nurls;
119 };
120
121 /*
122 * store into context data
123 */
124 static int
store_method_query(AXP * axp,int when,int type,int tag,const char * buf,size_t len,void * u)125 store_method_query(AXP *axp, int when, int type, int tag, const char *buf,
126 size_t len, void *u)
127 {
128 tr_ctx_t *tr_ctx = u;
129 struct method_query_arg *arg = tr_ctx->arg;
130 arms_context_t *res = arms_get_context();
131
132 if (when != AXP_PARSE_END)
133 return 0;
134
135 switch (tag) {
136 case ARMS_TAG_PUSH_METHOD:
137 if (arg->nmethods >= MAX_METHOD_INFO) {
138 /* error: max 5 methods */
139 tr_ctx->res_result = 203;/*Parameter Problem*/
140 tr_ctx->read_done = 1;
141 return -1;
142 }
143 if (!strcmp(buf, "https-simple")) {
144 arg->method_info[arg->nmethods++] = ARMS_PUSH_METHOD_SIMPLE;
145 } else if (!strcmp(buf, "https-tunnel")) {
146 arg->method_info[arg->nmethods++] = ARMS_PUSH_METHOD_TUNNEL;
147 } else {
148 /* unknown method type, ignore. */
149 libarms_log(ARMS_LOG_DEBUG,
150 "unknown method %s, ignored", buf);
151 }
152 break;
153 case ARMS_TAG_TUNNEL_URL:
154 if (arg->nurls >= MAX_RS_INFO) {
155 /* error: max 5 RSs */
156 tr_ctx->res_result = 203;/*Parameter Problem*/
157 tr_ctx->read_done = 1;
158 return -1;
159 }
160 res->rs_tunnel_url[arg->nurls++] = STRDUP(buf);
161 break;
162 case ARMS_TAG_ECHO_INTERVAL:
163 #ifdef HAVE_STDINT_H
164 res->tunnel_echo_interval = (intptr_t)buf;
165 #else
166 res->tunnel_echo_interval = (int)buf;
167 #endif
168 break;
169 case ARMS_TAG_MSG:
170 tr_ctx->read_done = 1;
171 break;
172 default:
173 break;
174 }
175 return 0;
176 }
177
178 static void *
method_query_context(tr_ctx_t * tr_ctx)179 method_query_context(tr_ctx_t *tr_ctx)
180 {
181 struct method_query_arg *ctx;
182
183 ctx = CALLOC(1, sizeof(struct method_query_arg));
184 if (ctx == NULL) {
185 /* not enough memory. */
186 } else {
187 /* create response parser */
188 ctx->axp = axp_create(method_query_res_msg,
189 "US-ASCII", tr_ctx, 0);
190 if (ctx->axp == NULL) {
191 /* error */
192 FREE(ctx);
193 return NULL;
194 }
195 MSETPOS(ctx->axp);
196 }
197 return ctx;
198 }
199
200 static void
method_query_release(tr_ctx_t * tr_ctx)201 method_query_release(tr_ctx_t *tr_ctx)
202 {
203 if (tr_ctx->arg) {
204 struct method_query_arg *arg = tr_ctx->arg;
205
206 if (arg->axp != NULL) {
207 axp_destroy(arg->axp);
208 arg->axp = NULL;
209 }
210 FREE(tr_ctx->arg);
211 }
212 }
213
214 /*
215 * Method implementations
216 */
217
218 /*
219 * send capability.
220 */
221 static int
method_query_request(transaction * tr,char * buf,int len,int * wrote)222 method_query_request(transaction *tr, char *buf, int len, int *wrote)
223 {
224 arms_context_t *res = arms_get_context();
225 int total, size;
226
227 total = size = arms_write_begin_message(tr, buf, len);
228 buf += size;
229 len -= size;
230
231 if (!res->proxy_is_available) {
232 size = snprintf(buf, len,
233 "<push-method>https-simple</push-method>");
234 buf += size;
235 len -= size;
236 total += size;
237 }
238 size = snprintf(buf, len, "<push-method>https-tunnel</push-method>");
239 buf += size;
240 len -= size;
241 total += size;
242 total += arms_write_end_message(tr, buf, len);
243
244 *wrote = total;
245 return TR_WRITE_DONE;
246 }
247
248 /*
249 */
250 static int
method_query_parse(transaction * tr,const char * buf,int len)251 method_query_parse(transaction *tr, const char *buf, int len)
252 {
253 tr_ctx_t *tr_ctx = &tr->tr_ctx;
254 struct method_query_arg *ctx;
255 int err;
256
257 if (buf == NULL) {
258 tr_ctx->read_done = 1;
259 return TR_READ_DONE;
260 }
261
262 ctx = tr_ctx->arg;
263 /* check result code */
264 if (!tr_ctx->read_done) {
265 err = axp_parse(ctx->axp, buf, len);
266 if (err < 0) {
267 return TR_PARSE_ERROR;
268 }
269 }
270 if (tr_ctx->read_done) {
271 err = axp_endparse(ctx->axp);
272 if (err != 0) {
273 tr_ctx->res_result = 200;
274 return TR_PARSE_ERROR;
275 }
276 /* read done. judgement! */
277 return method_query_judgement(tr, ctx->axp);
278 }
279 return TR_WANT_READ;
280 }
281
282 /*
283 * check result code and set method information into res.
284 */
285 static int
method_query_judgement(transaction * tr,AXP * axp)286 method_query_judgement(transaction *tr, AXP *axp)
287 {
288 tr_ctx_t *tr_ctx = &tr->tr_ctx;
289 arms_context_t *res = arms_get_context();
290 struct method_query_arg *arg;
291 int err, rcode;
292
293 arg = tr_ctx->arg;
294 /* check result */
295 err = axp_refer(axp, ARMS_TAG_RCODE, &rcode);
296 if (err != 0) {
297 tr_ctx->result = 402;/*SA failure*/
298 return TR_WANT_RETRY;
299 }
300 tr_ctx->res_result = rcode;
301
302 if (rcode >= 300 && rcode < 500) {
303 return TR_WANT_RETRY;
304 }
305 if (rcode >= 500) {
306 res->result = ARMS_EREBOOT;
307 switch (rcode) {
308 case 501:
309 res->result = ARMS_EDONTRETRY;
310 break;
311 case 502:
312 res->result = ARMS_EPULL;
313 break;
314 case 503:
315 res->result = ARMS_EREBOOT;
316 break;
317 case 507:
318 /*
319 * invalid type: older RS response.
320 * compatible method: simple and push-ready.
321 */
322 res->result = 0;
323 break;
324 case 508:
325 /* State Mismatch. */
326 res->result = ARMS_EPULL;
327 break;
328 }
329 return TR_WANT_STOP;
330 }
331 if (rcode >= 200) {
332 res->result = ARMS_EPULL;
333 return TR_WANT_STOP;
334 }
335 res->nmethods = arg->nmethods;
336 memcpy(res->method_info, arg->method_info, sizeof(res->method_info));
337
338 return TR_READ_DONE;
339 }
340