net_dns.c 12 KB

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