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