net_dns.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. */
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <limits.h>
  38. #include <misc/offset.h>
  39. #include <misc/bsort.h>
  40. #include <misc/balloc.h>
  41. #include <misc/compare.h>
  42. #include <structure/LinkedList1.h>
  43. #include <ncd/NCDModule.h>
  44. #include <ncd/NCDIfConfig.h>
  45. #include <ncd/extra/value_utils.h>
  46. #include <generated/blog_channel_ncd_net_dns.h>
  47. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  48. struct instance {
  49. NCDModuleInst *i;
  50. LinkedList1 ipv4_dns_servers;
  51. LinkedList1Node instances_node; // node in instances
  52. };
  53. struct ipv4_dns_entry {
  54. LinkedList1Node list_node; // node in instance.ipv4_dns_servers
  55. uint32_t addr;
  56. int priority;
  57. };
  58. static LinkedList1 instances;
  59. static struct ipv4_dns_entry * add_ipv4_dns_entry (struct instance *o, uint32_t addr, int priority)
  60. {
  61. // allocate entry
  62. struct ipv4_dns_entry *entry = malloc(sizeof(*entry));
  63. if (!entry) {
  64. return NULL;
  65. }
  66. // set info
  67. entry->addr = addr;
  68. entry->priority = priority;
  69. // add to list
  70. LinkedList1_Append(&o->ipv4_dns_servers, &entry->list_node);
  71. return entry;
  72. }
  73. static void remove_ipv4_dns_entry (struct instance *o, struct ipv4_dns_entry *entry)
  74. {
  75. // remove from list
  76. LinkedList1_Remove(&o->ipv4_dns_servers, &entry->list_node);
  77. // free entry
  78. free(entry);
  79. }
  80. static void remove_ipv4_dns_entries (struct instance *o)
  81. {
  82. LinkedList1Node *n;
  83. while (n = LinkedList1_GetFirst(&o->ipv4_dns_servers)) {
  84. struct ipv4_dns_entry *e = UPPER_OBJECT(n, struct ipv4_dns_entry, list_node);
  85. remove_ipv4_dns_entry(o, e);
  86. }
  87. }
  88. static size_t num_servers (void)
  89. {
  90. size_t c = 0;
  91. for (LinkedList1Node *n = LinkedList1_GetFirst(&instances); n; n = LinkedList1Node_Next(n)) {
  92. struct instance *o = UPPER_OBJECT(n, struct instance, instances_node);
  93. for (LinkedList1Node *en = LinkedList1_GetFirst(&o->ipv4_dns_servers); en; en = LinkedList1Node_Next(en)) {
  94. c++;
  95. }
  96. }
  97. return c;
  98. }
  99. struct dns_sort_entry {
  100. uint32_t addr;
  101. int priority;
  102. };
  103. static int dns_sort_comparator (const void *v1, const void *v2)
  104. {
  105. const struct dns_sort_entry *e1 = v1;
  106. const struct dns_sort_entry *e2 = v2;
  107. return B_COMPARE(e1->priority, e2->priority);
  108. }
  109. static int set_servers (void)
  110. {
  111. int ret = 0;
  112. // count servers
  113. size_t num_ipv4_dns_servers = num_servers();
  114. // allocate sort array
  115. struct dns_sort_entry *servers = BAllocArray(num_ipv4_dns_servers, sizeof(servers[0]));
  116. if (!servers) {
  117. goto fail0;
  118. }
  119. size_t num_servers = 0;
  120. // fill sort array
  121. for (LinkedList1Node *n = LinkedList1_GetFirst(&instances); n; n = LinkedList1Node_Next(n)) {
  122. struct instance *o = UPPER_OBJECT(n, struct instance, instances_node);
  123. for (LinkedList1Node *en = LinkedList1_GetFirst(&o->ipv4_dns_servers); en; en = LinkedList1Node_Next(en)) {
  124. struct ipv4_dns_entry *e = UPPER_OBJECT(en, struct ipv4_dns_entry, list_node);
  125. servers[num_servers].addr = e->addr;
  126. servers[num_servers].priority= e->priority;
  127. num_servers++;
  128. }
  129. }
  130. ASSERT(num_servers == num_ipv4_dns_servers)
  131. // sort by priority
  132. // use a custom insertion sort instead of qsort() because we want a stable sort
  133. struct dns_sort_entry sort_temp;
  134. BInsertionSort(servers, num_servers, sizeof(servers[0]), dns_sort_comparator, &sort_temp);
  135. // copy addresses into an array
  136. uint32_t *addrs = BAllocArray(num_servers, sizeof(addrs[0]));
  137. if (!addrs) {
  138. goto fail1;
  139. }
  140. for (size_t i = 0; i < num_servers; i++) {
  141. addrs[i] = servers[i].addr;
  142. }
  143. // set servers
  144. if (!NCDIfConfig_set_dns_servers(addrs, num_servers)) {
  145. goto fail2;
  146. }
  147. ret = 1;
  148. fail2:
  149. BFree(addrs);
  150. fail1:
  151. BFree(servers);
  152. fail0:
  153. return ret;
  154. }
  155. static int func_globalinit (const struct NCDModuleInst_iparams *params)
  156. {
  157. LinkedList1_Init(&instances);
  158. return 1;
  159. }
  160. static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
  161. {
  162. struct instance *o = vo;
  163. o->i = i;
  164. // init servers list
  165. LinkedList1_Init(&o->ipv4_dns_servers);
  166. // get arguments
  167. NCDValRef servers_arg;
  168. NCDValRef priority_arg;
  169. if (!NCDVal_ListRead(params->args, 2, &servers_arg, &priority_arg)) {
  170. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  171. goto fail1;
  172. }
  173. if (!NCDVal_IsList(servers_arg) || !NCDVal_IsString(priority_arg)) {
  174. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  175. goto fail1;
  176. }
  177. uintmax_t priority;
  178. if (!ncd_read_uintmax(priority_arg, &priority) || priority > INT_MAX) {
  179. ModuleLog(o->i, BLOG_ERROR, "wrong priority");
  180. goto fail1;
  181. }
  182. // read servers
  183. size_t count = NCDVal_ListCount(servers_arg);
  184. for (size_t j = 0; j < count; j++) {
  185. NCDValRef server_arg = NCDVal_ListGet(servers_arg, j);
  186. if (!NCDVal_IsString(server_arg)) {
  187. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  188. goto fail1;
  189. }
  190. uint32_t addr;
  191. if (!ipaddr_parse_ipv4_addr_bin((char *)NCDVal_StringData(server_arg), NCDVal_StringLength(server_arg), &addr)) {
  192. ModuleLog(o->i, BLOG_ERROR, "wrong addr");
  193. goto fail1;
  194. }
  195. if (!add_ipv4_dns_entry(o, addr, priority)) {
  196. ModuleLog(o->i, BLOG_ERROR, "failed to add dns entry");
  197. goto fail1;
  198. }
  199. }
  200. // add to instances
  201. LinkedList1_Append(&instances, &o->instances_node);
  202. // set servers
  203. if (!set_servers()) {
  204. ModuleLog(o->i, BLOG_ERROR, "failed to set DNS servers");
  205. goto fail2;
  206. }
  207. // signal up
  208. NCDModuleInst_Backend_Up(o->i);
  209. return;
  210. fail2:
  211. LinkedList1_Remove(&instances, &o->instances_node);
  212. fail1:
  213. remove_ipv4_dns_entries(o);
  214. NCDModuleInst_Backend_DeadError(i);
  215. }
  216. static void func_die (void *vo)
  217. {
  218. struct instance *o = vo;
  219. // remove from instances
  220. LinkedList1_Remove(&instances, &o->instances_node);
  221. // set servers
  222. set_servers();
  223. // free servers
  224. remove_ipv4_dns_entries(o);
  225. NCDModuleInst_Backend_Dead(o->i);
  226. }
  227. static struct NCDModule modules[] = {
  228. {
  229. .type = "net.dns",
  230. .func_new2 = func_new,
  231. .func_die = func_die,
  232. .alloc_size = sizeof(struct instance)
  233. }, {
  234. .type = NULL
  235. }
  236. };
  237. const struct NCDModuleGroup ncdmodule_net_dns = {
  238. .func_globalinit = func_globalinit,
  239. .modules = modules
  240. };