1 /*	$Id: proto_push_confirmation.c 20894 2012-01-25 12:47:57Z 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-confirmation message.
46  *
47  * 2way message, but special.
48  * - confirm-start is sent from SA.
49  * - confirm-done is sent from RS.
50  *
51  * workaround:
52  *  confirm-start as pull message.  mostly like as push-ready.
53  *  confirm-done as 1way push message.
54  */
55 
56 static int store_tag(AXP *axp, int when, int type, int tag,
57 		const char *buf, size_t len, void *u);
58 
59 
60 static void *confirm_start_context(tr_ctx_t *);
61 static void confirm_start_release(tr_ctx_t *);
62 static int confirm_start_request(transaction *, char *, int, int *);
63 static int confirm_start_parse(transaction *, const char *, int);
64 static int confirm_done_cparg(AXP *, uint32_t, int, const char *,
65 			      size_t, tr_ctx_t *);
66 static void confirm_done_release(tr_ctx_t *);
67 
68 arms_method_t confirm_start_methods = {
69 	ARMS_TR_CONFIRM_START,	/* pm_type */
70 	"push-confirmation-start",/* pm_string */
71 	NULL,			/* pm_schema */
72 	0,			/* pm_flags */
73 	NULL,			/* pm_response */
74 	confirm_start_request,	/* pm_done */
75 	NULL,			/* pm_exec */
76 	NULL,			/* pm_copyarg */
77 	NULL,			/* rollback */
78 	confirm_start_context,	/* pm_context */
79 	confirm_start_release,	/* pm_release */
80 	confirm_start_parse,	/* pm_parse */
81 };
82 
83 arms_method_t confirm_done_methods = {
84 	ARMS_TR_CONFIRM_DONE,	/* pm_type */
85 	"push-confirmation-done",/* pm_string */
86 	NULL,			/* pm_schema */
87 	0,			/* pm_flags */
88 	build_generic_res,	/* pm_response */
89 	NULL,			/* pm_done */
90 	NULL,			/* pm_exec */
91 	confirm_done_cparg,	/* pm_copyarg */
92 	NULL,			/* rollback */
93 	NULL,			/* pm_context */
94 	confirm_done_release	/* pm_release */
95 };
96 
97 typedef struct confirm_context {
98 	AXP *parse;
99 } confirm_context_t;
100 
101 static char *confirm_res_attr[] = {
102 	"type", NULL,
103 	NULL
104 };
105 
106 static struct axp_schema confirm_res_body[] = {
107 	/* id, tag, type, attr, callback, child */
108 	{ARMS_TAG_RCODE, "result-code", AXP_TYPE_INT,  NULL, NULL, NULL},
109 	{ARMS_TAG_RDESC, "description", AXP_TYPE_TEXT, NULL, NULL, NULL},
110 	{ARMS_TAG_TRANSACTION_ID, "transaction-id", AXP_TYPE_INT, NULL, NULL, NULL},
111 
112 	{0, NULL, 0, NULL, NULL, NULL}
113 };
114 static struct axp_schema arms_confirm_res[] = {
115 	/* id, tag, type, attr, callback, child */
116 	{ARMS_TAG_RES, "arms-response", AXP_TYPE_CHILD,
117 	 confirm_res_attr, NULL, confirm_res_body},
118 
119 	{0, NULL, 0, NULL, NULL, NULL}
120 };
121 
122 /* exported.  used by parse_configure_done.  */
123 struct axp_schema arms_confirm_res_msg[] = {
124 	/* id, tag, type, attr, callback, child */
125 	{ARMS_TAG_MSG, "arms-message", AXP_TYPE_CHILD,
126 		NULL, store_tag, arms_confirm_res},
127 
128 	{0, NULL, 0, NULL, NULL, NULL}
129 };
130 
131 /* tag parser <arms-message> for push-confirmation-start response. */
132 static int
store_tag(AXP * axp,int when,int type,int tag,const char * buf,size_t len,void * u)133 store_tag(AXP *axp, int when, int type, int tag,
134 		const char *buf, size_t len, void *u)
135 {
136 	tr_ctx_t *tr_ctx = u;
137 
138 	/* Emergency stop requested */
139 	if (tr_ctx->read_done) {
140 		return 0;
141 	}
142 
143 	if (when == AXP_PARSE_END)
144 		tr_ctx->read_done = 1;
145 	return 0;
146 }
147 
148 static void *
confirm_start_context(tr_ctx_t * tr_ctx)149 confirm_start_context(tr_ctx_t *tr_ctx)
150 {
151 	confirm_context_t *arg;
152 
153 	libarms_log(ARMS_LOG_DEBUG, "Start confirmation");
154 	arg = CALLOC(1, sizeof(*arg));
155 	if (arg != NULL) {
156 		arg->parse = axp_create(arms_confirm_res_msg,
157 					"US-ASCII", tr_ctx, 0);
158 	}
159 	return arg;
160 }
161 
162 static void
confirm_start_release(tr_ctx_t * tr_ctx)163 confirm_start_release(tr_ctx_t *tr_ctx)
164 {
165 	confirm_context_t *ctx;
166 
167 	if (tr_ctx->arg) {
168 		ctx = tr_ctx->arg;
169 		if (ctx->parse != NULL) {
170 			axp_destroy(ctx->parse);
171 			ctx->parse = NULL;
172 		}
173 		FREE(tr_ctx->arg);
174 		tr_ctx->arg = NULL;
175 	}
176 	libarms_log(ARMS_LOG_DEBUG,
177 	    "Sent confirmation request.  wait for response.");
178 }
179 
180 static int
confirm_start_request(transaction * tr,char * buf,int len,int * wrote)181 confirm_start_request(transaction *tr, char *buf, int len, int *wrote)
182 {
183 	arms_context_t *res = arms_get_context();
184 	int size, total;
185 
186 	total = size = arms_write_begin_message(tr, buf, len);
187 	buf += size;
188 	len -= size;
189 
190 	if (res->cur_method == ARMS_PUSH_METHOD_SIMPLE) {
191 		size = snprintf(buf, len,
192 				"<push-method>https-simple</push-method>"
193 				"<push-endpoint>%s</push-endpoint>",
194 				res->push_endpoint);
195 	} else if (res->cur_method == ARMS_PUSH_METHOD_TUNNEL) {
196 		size = snprintf(buf, len,
197 				"<push-method>https-tunnel</push-method>");
198 	}
199 	buf += size;
200 	len -= size;
201 	total += size;
202 	total += arms_write_end_message(tr, buf, len);
203 
204 	tr->tr_ctx.read_done = 0;
205 
206 	*wrote = total;
207 	return TR_WRITE_DONE;
208 }
209 
210 static int
confirm_start_parse(transaction * tr,const char * buf,int len)211 confirm_start_parse(transaction *tr, const char *buf, int len)
212 {
213 	tr_ctx_t *tr_ctx = &tr->tr_ctx;
214 	arms_context_t *res = arms_get_context();
215 	confirm_context_t *ctx = tr_ctx->arg;
216 	int err;
217 
218 	if (!tr_ctx->read_done) {
219 		tr_ctx->res_result = 100;
220 		err = axp_parse(ctx->parse, buf, len);
221 		if (err < 0) {
222 			return TR_PARSE_ERROR;
223 		}
224 	}
225 	if (tr_ctx->read_done) {
226 		int rcode;
227 
228 		err = axp_endparse(ctx->parse);
229 		if (err != 0) {
230 			tr_ctx->res_result = 200;
231 			return TR_PARSE_ERROR;
232 		}
233 		/* refer transaction id */
234 		err = axp_refer(ctx->parse, ARMS_TAG_TRANSACTION_ID,
235 				&tr_ctx->id);
236 		if (err != 0) {
237 			return TR_PARSE_ERROR;
238 		}
239 		err = axp_refer(ctx->parse, ARMS_TAG_RCODE, &rcode);
240 		if (err != 0) {
241 			tr_ctx->result = 402;/*SA failure*/
242 			return TR_WANT_RETRY;
243 		}
244 		tr_ctx->res_result = rcode;
245 		if (rcode >= 300 && rcode < 500) {
246 			return TR_WANT_RETRY;
247 		}
248 		if (rcode >= 500) {
249 			res->result = ARMS_EREBOOT;
250 			switch (rcode) {
251 			case 501:
252 				res->result = ARMS_EDONTRETRY;
253 				break;
254 			case 502:
255 				res->result = ARMS_EPULL;
256 				break;
257 			case 503:
258 				res->result = ARMS_EREBOOT;
259 				break;
260 			case 507:
261 				/*
262 				 * invalid type: older RS response.
263 				 * compatible method: simple and push-ready.
264 				 */
265 				res->result = 0;
266 				break;
267 			case 508:
268 				/* State Mismatch. */
269 				res->result = ARMS_EPULL;
270 				break;
271 			}
272 			return TR_WANT_STOP;
273 		}
274 		if (rcode >= 200) {
275 			res->result = ARMS_EPULL;
276 			return TR_WANT_STOP;
277 		}
278 		return TR_READ_DONE;
279 	}
280 	return TR_WANT_READ;
281 }
282 
283 static int
confirm_done_cparg(AXP * axp,uint32_t pm_type,int tag,const char * buf,size_t len,tr_ctx_t * tr_ctx)284 confirm_done_cparg(AXP *axp, uint32_t pm_type, int tag,
285 		 const char *buf, size_t len, tr_ctx_t *tr_ctx)
286 {
287 	arms_context_t *res = arms_get_context();
288 	char *p;
289 	int id;
290 
291 	switch (tag) {
292 	case ARMS_TAG_START_CPARG:
293 		break;
294 	case ARMS_TAG_END_CPARG:
295 		break;
296 	case ARMS_TAG_TRANSACTION_ID:
297 		/* compare transaction id */
298 		id = strtoul(buf, &p, 10);
299 		if (*p != '\0')
300 			return -1;
301 		if (res->cur_method == ARMS_PUSH_METHOD_SIMPLE) {
302 			/* do nothing. */
303 		} else if (res->cur_method == ARMS_PUSH_METHOD_TUNNEL) {
304 			/* tunnel: parallel confirmation.  lookup from tr. */
305 			tr_ctx->id = id;
306 		}
307 		break;
308 	default:
309 		break;
310 	}
311 	return 0;
312 }
313 
314 static void
confirm_done_release(tr_ctx_t * tr_ctx)315 confirm_done_release(tr_ctx_t *tr_ctx)
316 {
317 	arms_context_t *res = arms_get_context();
318 
319 	/* disable watchdog */
320 	res->confirm_id = 0;
321 	/* set global state */
322 	arms_set_global_state(ARMS_ST_PUSH_WAIT);
323 	/* start heartbeat */
324 	arms_hb_start_loop(res);
325 }
326