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