NCDRequest.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /**
  2. * @file NCDRequest.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. #include <stddef.h>
  30. #include <stdint.h>
  31. #include <limits.h>
  32. #include <misc/byteorder.h>
  33. #include <misc/expstring.h>
  34. #include <protocol/packetproto.h>
  35. #include <protocol/requestproto.h>
  36. #include <base/BLog.h>
  37. #include "NCDRequest.h"
  38. #include <generated/blog_channel_NCDRequest.h>
  39. #define SEND_PAYLOAD_MTU 32768
  40. #define RECV_PAYLOAD_MTU 32768
  41. #define SEND_MTU (SEND_PAYLOAD_MTU + sizeof(struct requestproto_header))
  42. #define RECV_MTU (RECV_PAYLOAD_MTU + sizeof(struct requestproto_header))
  43. #define STATE_CONNECTING 1
  44. #define STATE_CONNECTED 2
  45. static int build_requestproto_packet (uint32_t request_id, uint32_t flags, NCDValue *payload_value, uint8_t **out_data, int *out_len);
  46. static void report_finished (NCDRequest *o, int is_error);
  47. static void connector_handler (NCDRequest *o, int is_error);
  48. static void connection_handler (NCDRequest *o, int event);
  49. static void decoder_handler_error (NCDRequest *o);
  50. static void recv_if_handler_send (NCDRequest *o, uint8_t *data, int data_len);
  51. static void send_sender_iface_handler_done (NCDRequest *o);
  52. static int build_requestproto_packet (uint32_t request_id, uint32_t flags, NCDValue *payload_value, uint8_t **out_data, int *out_len)
  53. {
  54. struct header {
  55. struct packetproto_header pp;
  56. struct requestproto_header rp;
  57. };
  58. ExpString str;
  59. if (!ExpString_Init(&str)) {
  60. BLog(BLOG_ERROR, "ExpString_Init failed");
  61. goto fail0;
  62. }
  63. if (!ExpString_AppendZeros(&str, sizeof(struct header))) {
  64. BLog(BLOG_ERROR, "ExpString_AppendBinary failed");
  65. goto fail1;
  66. }
  67. if (payload_value && !NCDValueGenerator_AppendGenerate(payload_value, &str)) {
  68. BLog(BLOG_ERROR, "NCDValueGenerator_AppendGenerate failed");
  69. goto fail1;
  70. }
  71. size_t len = ExpString_Length(&str);
  72. if (len > INT_MAX || len > PACKETPROTO_ENCLEN(SEND_MTU) || len - sizeof(struct packetproto_header) > UINT16_MAX) {
  73. BLog(BLOG_ERROR, "reply is too long");
  74. goto fail1;
  75. }
  76. uint8_t *packet = ExpString_Get(&str);
  77. struct header *header = (void *)packet;
  78. header->pp.len = htol16(len - sizeof(struct packetproto_header));
  79. header->rp.request_id = htol32(request_id);
  80. header->rp.flags = htol32(flags);
  81. *out_data = packet;
  82. *out_len = len;
  83. return 1;
  84. fail1:
  85. ExpString_Free(&str);
  86. fail0:
  87. return 0;
  88. }
  89. static void report_finished (NCDRequest *o, int is_error)
  90. {
  91. DEBUGERROR(&o->d_err, o->handler_finished(o->user, is_error))
  92. }
  93. static void connector_handler (NCDRequest *o, int is_error)
  94. {
  95. DebugObject_Access(&o->d_obj);
  96. ASSERT(o->state == STATE_CONNECTING)
  97. // check error
  98. if (is_error) {
  99. BLog(BLOG_ERROR, "failed to connect to socket");
  100. goto fail0;
  101. }
  102. BPendingGroup *pg = BReactor_PendingGroup(o->reactor);
  103. // init connection
  104. if (!BConnection_Init(&o->con, BCONNECTION_SOURCE_CONNECTOR(&o->connector), o->reactor, o, (BConnection_handler)connection_handler)) {
  105. BLog(BLOG_ERROR, "BConnection_Init failed");
  106. goto fail0;
  107. }
  108. // init connection interfaces
  109. BConnection_SendAsync_Init(&o->con);
  110. BConnection_RecvAsync_Init(&o->con);
  111. StreamPassInterface *con_send_if = BConnection_SendAsync_GetIf(&o->con);
  112. StreamRecvInterface *con_recv_if = BConnection_RecvAsync_GetIf(&o->con);
  113. // init receive interface
  114. PacketPassInterface_Init(&o->recv_if, RECV_MTU, (PacketPassInterface_handler_send)recv_if_handler_send, o, pg);
  115. // init receive decoder
  116. if (!PacketProtoDecoder_Init(&o->recv_decoder, con_recv_if, &o->recv_if, pg, o, (PacketProtoDecoder_handler_error)decoder_handler_error)) {
  117. BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed");
  118. goto fail1;
  119. }
  120. // init send sender
  121. PacketStreamSender_Init(&o->send_sender, con_send_if, PACKETPROTO_ENCLEN(SEND_MTU), pg);
  122. o->send_sender_iface = PacketStreamSender_GetInput(&o->send_sender);
  123. // init send interface
  124. PacketPassInterface_Sender_Init(o->send_sender_iface, (PacketPassInterface_handler_done)send_sender_iface_handler_done, o);
  125. // send request
  126. PacketPassInterface_Sender_Send(o->send_sender_iface, o->request_data, o->request_len);
  127. // set state connected
  128. o->state = STATE_CONNECTED;
  129. return;
  130. fail1:
  131. PacketPassInterface_Free(&o->recv_if);
  132. BConnection_RecvAsync_Free(&o->con);
  133. BConnection_SendAsync_Free(&o->con);
  134. BConnection_Free(&o->con);
  135. fail0:
  136. report_finished(o, 1);
  137. }
  138. static void connection_handler (NCDRequest *o, int event)
  139. {
  140. DebugObject_Access(&o->d_obj);
  141. ASSERT(o->state == STATE_CONNECTED)
  142. BLog(BLOG_ERROR, "connection error");
  143. report_finished(o, 1);
  144. }
  145. static void decoder_handler_error (NCDRequest *o)
  146. {
  147. DebugObject_Access(&o->d_obj);
  148. ASSERT(o->state == STATE_CONNECTED)
  149. BLog(BLOG_ERROR, "decoder error");
  150. report_finished(o, 1);
  151. }
  152. static void recv_if_handler_send (NCDRequest *o, uint8_t *data, int data_len)
  153. {
  154. DebugObject_Access(&o->d_obj);
  155. ASSERT(o->state == STATE_CONNECTED)
  156. ASSERT(!o->processing)
  157. ASSERT(data_len >= 0)
  158. ASSERT(data_len <= RECV_MTU)
  159. if (data_len < sizeof(struct requestproto_header)) {
  160. BLog(BLOG_ERROR, "missing requestproto header");
  161. goto fail;
  162. }
  163. struct requestproto_header *header = (struct requestproto_header *)data;
  164. uint32_t request_id = ltoh32(header->request_id);
  165. uint32_t flags = ltoh32(header->flags);
  166. uint8_t *payload = data + sizeof(*header);
  167. int payload_len = data_len - sizeof(*header);
  168. if (request_id != o->request_id) {
  169. BLog(BLOG_ERROR, "invalid request ID");
  170. goto fail;
  171. }
  172. if (flags == REQUESTPROTO_REPLY_FLAG_DATA) {
  173. NCDValue value;
  174. if (!NCDValueParser_Parse(payload, payload_len, &value)) {
  175. BLog(BLOG_ERROR, "NCDValueParser_Parse failed");
  176. goto fail;
  177. }
  178. // set processing
  179. o->processing = 1;
  180. // call reply handler
  181. o->handler_reply(o->user, value);
  182. return;
  183. }
  184. if (flags == REQUESTPROTO_REPLY_FLAG_END) {
  185. if (payload_len != 0) {
  186. BLog(BLOG_ERROR, "end reply has non-empty payload");
  187. goto fail;
  188. }
  189. // call finished handler
  190. report_finished(o, 0);
  191. return;
  192. }
  193. BLog(BLOG_ERROR, "invalid requestproto flags");
  194. fail:
  195. report_finished(o, 1);
  196. }
  197. static void send_sender_iface_handler_done (NCDRequest *o)
  198. {
  199. DebugObject_Access(&o->d_obj);
  200. ASSERT(o->state == STATE_CONNECTED)
  201. }
  202. int NCDRequest_Init (NCDRequest *o, const char *socket_path, NCDValue *payload_value, BReactor *reactor, void *user, NCDRequest_handler_finished handler_finished, NCDRequest_handler_reply handler_reply)
  203. {
  204. ASSERT(socket_path)
  205. NCDValue_Type(payload_value);
  206. ASSERT(handler_finished)
  207. ASSERT(handler_reply)
  208. // init arguments
  209. o->reactor = reactor;
  210. o->user = user;
  211. o->handler_finished = handler_finished;
  212. o->handler_reply = handler_reply;
  213. // choose request ID
  214. o->request_id = 175;
  215. // build request
  216. if (!build_requestproto_packet(o->request_id, REQUESTPROTO_REQUEST_FLAG, payload_value, &o->request_data, &o->request_len)) {
  217. BLog(BLOG_ERROR, "failed to build request");
  218. goto fail0;
  219. }
  220. // init connector
  221. if (!BConnector_InitUnix(&o->connector, socket_path, reactor, o, (BConnector_handler)connector_handler)) {
  222. BLog(BLOG_ERROR, "BConnector_InitUnix failed");
  223. goto fail1;
  224. }
  225. // set state connecting
  226. o->state = STATE_CONNECTING;
  227. // set not processing
  228. o->processing = 0;
  229. DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
  230. DebugObject_Init(&o->d_obj);
  231. return 1;
  232. fail1:
  233. free(o->request_data);
  234. fail0:
  235. return 0;
  236. }
  237. void NCDRequest_Free (NCDRequest *o)
  238. {
  239. DebugObject_Free(&o->d_obj);
  240. DebugError_Free(&o->d_err);
  241. if (o->state == STATE_CONNECTED) {
  242. PacketStreamSender_Free(&o->send_sender);
  243. PacketProtoDecoder_Free(&o->recv_decoder);
  244. PacketPassInterface_Free(&o->recv_if);
  245. BConnection_RecvAsync_Free(&o->con);
  246. BConnection_SendAsync_Free(&o->con);
  247. BConnection_Free(&o->con);
  248. }
  249. BConnector_Free(&o->connector);
  250. free(o->request_data);
  251. }
  252. void NCDRequest_Next (NCDRequest *o)
  253. {
  254. DebugObject_Access(&o->d_obj);
  255. ASSERT(o->state == STATE_CONNECTED)
  256. ASSERT(o->processing)
  257. // set not processing
  258. o->processing = 0;
  259. // accept received packet
  260. PacketPassInterface_Done(&o->recv_if);
  261. }