net_dns.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /**
  2. * @file net_dns.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. * @section DESCRIPTION
  30. *
  31. * DNS servers module.
  32. *
  33. * Synopsis: net.dns(list(string) servers, string priority)
  34. * Synopsis: net.dns.resolvconf(list({string type, string value}) lines, string priority)
  35. */
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <limits.h>
  39. #include <misc/offset.h>
  40. #include <misc/bsort.h>
  41. #include <misc/balloc.h>
  42. #include <misc/compare.h>
  43. #include <misc/concat_strings.h>
  44. #include <misc/expstring.h>
  45. #include <misc/ipaddr.h>
  46. #include <structure/LinkedList1.h>
  47. #include <ncd/extra/NCDIfConfig.h>
  48. #include <ncd/module_common.h>
  49. #include <generated/blog_channel_ncd_net_dns.h>
  50. struct instance {
  51. NCDModuleInst *i;
  52. LinkedList1 entries;
  53. LinkedList1Node instances_node; // node in instances
  54. };
  55. struct dns_entry {
  56. LinkedList1Node list_node; // node in instance.entries
  57. char *line;
  58. int priority;
  59. };
  60. struct global {
  61. LinkedList1 instances;
  62. };
  63. static struct dns_entry * add_dns_entry (struct instance *o, const char *type, const char *value, int priority)
  64. {
  65. // allocate entry
  66. struct dns_entry *entry = malloc(sizeof(*entry));
  67. if (!entry) {
  68. goto fail0;
  69. }
  70. // generate line
  71. entry->line = concat_strings(4, type, " ", value, "\n");
  72. if (!entry->line) {
  73. goto fail1;
  74. }
  75. // set info
  76. entry->priority = priority;
  77. // add to list
  78. LinkedList1_Append(&o->entries, &entry->list_node);
  79. return entry;
  80. fail1:
  81. free(entry);
  82. fail0:
  83. return NULL;
  84. }
  85. static void remove_dns_entry (struct instance *o, struct dns_entry *entry)
  86. {
  87. // remove from list
  88. LinkedList1_Remove(&o->entries, &entry->list_node);
  89. // free line
  90. free(entry->line);
  91. // free entry
  92. free(entry);
  93. }
  94. static void remove_entries (struct instance *o)
  95. {
  96. LinkedList1Node *n;
  97. while (n = LinkedList1_GetFirst(&o->entries)) {
  98. struct dns_entry *e = UPPER_OBJECT(n, struct dns_entry, list_node);
  99. remove_dns_entry(o, e);
  100. }
  101. }
  102. static size_t count_entries (struct global *g)
  103. {
  104. size_t c = 0;
  105. for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) {
  106. struct instance *o = UPPER_OBJECT(n, struct instance, instances_node);
  107. for (LinkedList1Node *en = LinkedList1_GetFirst(&o->entries); en; en = LinkedList1Node_Next(en)) {
  108. c++;
  109. }
  110. }
  111. return c;
  112. }
  113. struct dns_sort_entry {
  114. char *line;
  115. int priority;
  116. };
  117. static int dns_sort_comparator (const void *v1, const void *v2)
  118. {
  119. const struct dns_sort_entry *e1 = v1;
  120. const struct dns_sort_entry *e2 = v2;
  121. return B_COMPARE(e1->priority, e2->priority);
  122. }
  123. static int set_servers (struct global *g)
  124. {
  125. int ret = 0;
  126. // count servers
  127. size_t num_entries = count_entries(g);
  128. // allocate sort array
  129. struct dns_sort_entry *sort_entries = BAllocArray(num_entries, sizeof(sort_entries[0]));
  130. if (!sort_entries) {
  131. goto fail0;
  132. }
  133. // fill sort array
  134. num_entries = 0;
  135. for (LinkedList1Node *n = LinkedList1_GetFirst(&g->instances); n; n = LinkedList1Node_Next(n)) {
  136. struct instance *o = UPPER_OBJECT(n, struct instance, instances_node);
  137. for (LinkedList1Node *en = LinkedList1_GetFirst(&o->entries); en; en = LinkedList1Node_Next(en)) {
  138. struct dns_entry *e = UPPER_OBJECT(en, struct dns_entry, list_node);
  139. sort_entries[num_entries].line = e->line;
  140. sort_entries[num_entries].priority= e->priority;
  141. num_entries++;
  142. }
  143. }
  144. // sort by priority
  145. // use a custom insertion sort instead of qsort() because we want a stable sort
  146. struct dns_sort_entry temp;
  147. BInsertionSort(sort_entries, num_entries, sizeof(sort_entries[0]), dns_sort_comparator, &temp);
  148. ExpString estr;
  149. if (!ExpString_Init(&estr)) {
  150. goto fail1;
  151. }
  152. for (size_t i = 0; i < num_entries; i++) {
  153. if (!ExpString_Append(&estr, sort_entries[i].line)) {
  154. goto fail2;
  155. }
  156. }
  157. // set servers
  158. if (!NCDIfConfig_set_resolv_conf(ExpString_Get(&estr), ExpString_Length(&estr))) {
  159. goto fail2;
  160. }
  161. ret = 1;
  162. fail2:
  163. ExpString_Free(&estr);
  164. fail1:
  165. BFree(sort_entries);
  166. fail0:
  167. return ret;
  168. }
  169. static int func_globalinit (struct NCDInterpModuleGroup *group, const struct NCDModuleInst_iparams *params)
  170. {
  171. // allocate global state structure
  172. struct global *g = BAlloc(sizeof(*g));
  173. if (!g) {
  174. BLog(BLOG_ERROR, "BAlloc failed");
  175. return 0;
  176. }
  177. // set group state pointer
  178. group->group_state = g;
  179. // init instances list
  180. LinkedList1_Init(&g->instances);
  181. return 1;
  182. }
  183. static void func_globalfree (struct NCDInterpModuleGroup *group)
  184. {
  185. struct global *g = group->group_state;
  186. ASSERT(LinkedList1_IsEmpty(&g->instances))
  187. // free global state structure
  188. BFree(g);
  189. }
  190. static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  191. {
  192. struct global *g = ModuleGlobal(i);
  193. struct instance *o = vo;
  194. o->i = i;
  195. // init servers list
  196. LinkedList1_Init(&o->entries);
  197. // get arguments
  198. NCDValRef servers_arg;
  199. NCDValRef priority_arg;
  200. if (!NCDVal_ListRead(params->args, 2, &servers_arg, &priority_arg)) {
  201. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  202. goto fail1;
  203. }
  204. if (!NCDVal_IsList(servers_arg)) {
  205. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  206. goto fail1;
  207. }
  208. uintmax_t priority;
  209. if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) {
  210. ModuleLog(o->i, BLOG_ERROR, "wrong priority");
  211. goto fail1;
  212. }
  213. // read servers
  214. size_t count = NCDVal_ListCount(servers_arg);
  215. for (size_t j = 0; j < count; j++) {
  216. NCDValRef server_arg = NCDVal_ListGet(servers_arg, j);
  217. if (!NCDVal_IsString(server_arg)) {
  218. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  219. goto fail1;
  220. }
  221. uint32_t addr;
  222. if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(server_arg), NCDVal_StringLength(server_arg), &addr)) {
  223. ModuleLog(o->i, BLOG_ERROR, "wrong addr");
  224. goto fail1;
  225. }
  226. char addr_str[IPADDR_PRINT_MAX];
  227. ipaddr_print_addr(addr, addr_str);
  228. if (!add_dns_entry(o, "nameserver", addr_str, priority)) {
  229. ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry");
  230. goto fail1;
  231. }
  232. }
  233. // add to instances
  234. LinkedList1_Append(&g->instances, &o->instances_node);
  235. // set servers
  236. if (!set_servers(g)) {
  237. ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers");
  238. goto fail2;
  239. }
  240. // signal up
  241. NCDModuleInst_Backend_Up(o->i);
  242. return;
  243. fail2:
  244. LinkedList1_Remove(&g->instances, &o->instances_node);
  245. fail1:
  246. remove_entries(o);
  247. NCDModuleInst_Backend_DeadError(i);
  248. }
  249. static void func_new_resolvconf (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  250. {
  251. struct global *g = ModuleGlobal(i);
  252. struct instance *o = vo;
  253. o->i = i;
  254. // init servers list
  255. LinkedList1_Init(&o->entries);
  256. // get arguments
  257. NCDValRef lines_arg;
  258. NCDValRef priority_arg;
  259. if (!NCDVal_ListRead(params->args, 2, &lines_arg, &priority_arg)) {
  260. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  261. goto fail1;
  262. }
  263. if (!NCDVal_IsList(lines_arg)) {
  264. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  265. goto fail1;
  266. }
  267. uintmax_t priority;
  268. if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) {
  269. ModuleLog(o->i, BLOG_ERROR, "wrong priority");
  270. goto fail1;
  271. }
  272. // read lines
  273. size_t count = NCDVal_ListCount(lines_arg);
  274. for (size_t j = 0; j < count; j++) {
  275. int loop_failed = 1;
  276. NCDValRef line = NCDVal_ListGet(lines_arg, j);
  277. if (!NCDVal_IsList(line) || NCDVal_ListCount(line) != 2) {
  278. ModuleLog(o->i, BLOG_ERROR, "lines element is not a list with two elements");
  279. goto loop_fail0;
  280. }
  281. NCDValRef type = NCDVal_ListGet(line, 0);
  282. NCDValRef value = NCDVal_ListGet(line, 1);
  283. if (!NCDVal_IsStringNoNulls(type) || !NCDVal_IsStringNoNulls(value)) {
  284. ModuleLog(o->i, BLOG_ERROR, "wrong type of type or value");
  285. goto loop_fail0;
  286. }
  287. NCDValNullTermString type_nts;
  288. if (!NCDVal_StringNullTerminate(type, &type_nts)) {
  289. ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed");
  290. goto loop_fail0;
  291. }
  292. NCDValNullTermString value_nts;
  293. if (!NCDVal_StringNullTerminate(value, &value_nts)) {
  294. ModuleLog(o->i, BLOG_ERROR, "NCDVal_StringNullTerminate failed");
  295. goto loop_fail1;
  296. }
  297. if (!add_dns_entry(o, type_nts.data, value_nts.data, priority)) {
  298. ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry");
  299. goto loop_fail2;
  300. }
  301. loop_failed = 0;
  302. loop_fail2:
  303. NCDValNullTermString_Free(&value_nts);
  304. loop_fail1:
  305. NCDValNullTermString_Free(&type_nts);
  306. loop_fail0:
  307. if (loop_failed) {
  308. goto fail1;
  309. }
  310. }
  311. // add to instances
  312. LinkedList1_Append(&g->instances, &o->instances_node);
  313. // set servers
  314. if (!set_servers(g)) {
  315. ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers");
  316. goto fail2;
  317. }
  318. // signal up
  319. NCDModuleInst_Backend_Up(o->i);
  320. return;
  321. fail2:
  322. LinkedList1_Remove(&g->instances, &o->instances_node);
  323. fail1:
  324. remove_entries(o);
  325. NCDModuleInst_Backend_DeadError(i);
  326. }
  327. static void func_die (void *vo)
  328. {
  329. struct instance *o = vo;
  330. struct global *g = ModuleGlobal(o->i);
  331. // remove from instances
  332. LinkedList1_Remove(&g->instances, &o->instances_node);
  333. // set servers
  334. set_servers(g);
  335. // free servers
  336. remove_entries(o);
  337. NCDModuleInst_Backend_Dead(o->i);
  338. }
  339. static struct NCDModule modules[] = {
  340. {
  341. .type = "net.dns",
  342. .func_new2 = func_new,
  343. .func_die = func_die,
  344. .alloc_size = sizeof(struct instance)
  345. }, {
  346. .type = "net.dns.resolvconf",
  347. .func_new2 = func_new_resolvconf,
  348. .func_die = func_die,
  349. .alloc_size = sizeof(struct instance)
  350. }, {
  351. .type = NULL
  352. }
  353. };
  354. const struct NCDModuleGroup ncdmodule_net_dns = {
  355. .func_globalinit = func_globalinit,
  356. .func_globalfree = func_globalfree,
  357. .modules = modules
  358. };