GNUnet  0.10.x
dnsstub.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2012, 2018 GNUnet e.V.
4 
5  GNUnet is free software: you can redistribute it and/or modify it
6  under the terms of the GNU Affero General Public License as published
7  by the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  GNUnet is distributed in the hope that it will be useful, but
11  WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Affero General Public License for more details.
14 
15  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 
31 #define DNS_RETRANSMIT_DELAY \
32  GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
33 
34 
38 struct DnsServer;
39 
40 
49 
54 
59 
63  void *rc_cls;
64 
69 
74 
78  struct DnsServer *ds_pos;
79 
84 
88  void *request;
89 
93  size_t request_len;
94 };
95 
96 
100 struct DnsServer {
104  struct DnsServer *next;
105 
109  struct DnsServer *prev;
110 
114  struct sockaddr_storage ss;
115 };
116 
117 
126 
131 
136 
140  struct GNUNET_TIME_Relative retry_freq;
141 
145  unsigned int num_sockets;
146 };
147 
148 
154 static void
156 {
157  if (NULL != rs->dnsout4)
158  {
160  rs->dnsout4 = NULL;
161  }
162  if (NULL != rs->dnsout6)
163  {
165  rs->dnsout6 = NULL;
166  }
167  if (NULL != rs->read_task)
168  {
170  rs->read_task = NULL;
171  }
172  if (NULL != rs->retry_task)
173  {
175  rs->retry_task = NULL;
176  }
177  if (NULL != rs->request)
178  {
179  GNUNET_free(rs->request);
180  rs->request = NULL;
181  }
182 }
183 
184 
191 static struct GNUNET_NETWORK_Handle *
193 {
194  struct sockaddr_in a4;
195  struct sockaddr_in6 a6;
196  struct sockaddr *sa;
197  socklen_t alen;
198  struct GNUNET_NETWORK_Handle *ret;
199 
200  ret = GNUNET_NETWORK_socket_create(af, SOCK_DGRAM, 0);
201  if (NULL == ret)
202  return NULL;
203  switch (af)
204  {
205  case AF_INET:
206  memset(&a4, 0, alen = sizeof(struct sockaddr_in));
207  sa = (struct sockaddr *)&a4;
208  break;
209 
210  case AF_INET6:
211  memset(&a6, 0, alen = sizeof(struct sockaddr_in6));
212  sa = (struct sockaddr *)&a6;
213  break;
214 
215  default:
216  GNUNET_break(0);
218  return NULL;
219  }
220  sa->sa_family = af;
221  if (GNUNET_OK != GNUNET_NETWORK_socket_bind(ret, sa, alen))
222  {
224  _("Could not bind to any port: %s\n"),
225  strerror(errno));
227  return NULL;
228  }
229  return ret;
230 }
231 
232 
240 static struct GNUNET_DNSSTUB_RequestSocket *
242 {
243  struct GNUNET_DNSSTUB_RequestSocket *rs;
244 
245  for (unsigned int i = 0; i < 256; i++)
246  {
248  ctx->num_sockets)];
249  if (NULL == rs->rc)
250  break;
251  }
252  if (NULL != rs->rc)
253  {
254  /* signal "failure" */
255  rs->rc(rs->rc_cls, NULL, 0);
256  rs->rc = NULL;
257  }
258  if (NULL != rs->read_task)
259  {
261  rs->read_task = NULL;
262  }
263  if (NULL != rs->retry_task)
264  {
266  rs->retry_task = NULL;
267  }
268  if (NULL != rs->request)
269  {
270  GNUNET_free(rs->request);
271  rs->request = NULL;
272  }
273  rs->ctx = ctx;
274  return rs;
275 }
276 
277 
286 static int
288  struct GNUNET_NETWORK_Handle *dnsout)
289 {
290  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
291  ssize_t r;
292  int len;
293 
294  if (0 != ioctl(GNUNET_NETWORK_get_fd(dnsout), FIONREAD, &len))
295  {
296  /* conservative choice: */
297  len = UINT16_MAX;
298  }
299 
300  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving %d byte DNS reply\n", len);
301  {
302  unsigned char buf[len] GNUNET_ALIGN;
303  int found;
304  struct sockaddr_storage addr;
305  socklen_t addrlen;
306  struct GNUNET_TUN_DnsHeader *dns;
307 
308  addrlen = sizeof(addr);
309  memset(&addr, 0, sizeof(addr));
311  buf,
312  sizeof(buf),
313  (struct sockaddr *)&addr,
314  &addrlen);
315  if (-1 == r)
316  {
319  return GNUNET_SYSERR;
320  }
321  found = GNUNET_NO;
322  for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
323  {
324  if (0 == memcmp(&addr,
325  &ds->ss,
326  GNUNET_MIN(sizeof(struct sockaddr_storage), addrlen)))
327  {
328  found = GNUNET_YES;
329  break;
330  }
331  }
332  if (GNUNET_NO == found)
333  {
335  "Received DNS response from server we never asked (ignored)");
336  return GNUNET_NO;
337  }
338  if (sizeof(struct GNUNET_TUN_DnsHeader) > (size_t)r)
339  {
341  _("Received DNS response that is too small (%u bytes)"),
342  (unsigned int)r);
343  return GNUNET_NO;
344  }
345  dns = (struct GNUNET_TUN_DnsHeader *)buf;
346  if (NULL == rs->rc)
347  {
349  "Request timeout or cancelled; ignoring reply\n");
350  return GNUNET_NO;
351  }
352  rs->rc(rs->rc_cls, dns, r);
353  }
354  return GNUNET_OK;
355 }
356 
357 
363 static void
364 read_response(void *cls);
365 
366 
372 static void
374 {
375  struct GNUNET_NETWORK_FDSet *rset;
376 
377  if (NULL != rs->read_task)
380  if (NULL != rs->dnsout4)
382  if (NULL != rs->dnsout6)
384  rs->read_task =
387  rset,
388  NULL,
389  &read_response,
390  rs);
392 }
393 
394 
400 static void
401 read_response(void *cls)
402 {
403  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
404  const struct GNUNET_SCHEDULER_TaskContext *tc;
405 
406  rs->read_task = NULL;
408  /* read and process ready sockets */
409  if ((NULL != rs->dnsout4) &&
411  (GNUNET_SYSERR == do_dns_read(rs, rs->dnsout4)))
412  rs->dnsout4 = NULL;
413  if ((NULL != rs->dnsout6) &&
415  (GNUNET_SYSERR == do_dns_read(rs, rs->dnsout6)))
416  rs->dnsout6 = NULL;
417  /* re-schedule read task */
418  schedule_read(rs);
419 }
420 
421 
428 static void
429 transmit_query(void *cls)
430 {
431  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
432  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
433  const struct sockaddr *sa;
434  socklen_t salen;
435  struct DnsServer *ds;
436  struct GNUNET_NETWORK_Handle *dnsout;
437 
438  rs->retry_task =
440  ds = rs->ds_pos;
441  rs->ds_pos = ds->next;
442  if (NULL == rs->ds_pos)
443  rs->ds_pos = ctx->dns_head;
444  GNUNET_assert(NULL != ds);
445  dnsout = NULL;
446  switch (ds->ss.ss_family)
447  {
448  case AF_INET:
449  if (NULL == rs->dnsout4)
450  rs->dnsout4 = open_socket(AF_INET);
451  dnsout = rs->dnsout4;
452  sa = (const struct sockaddr *)&ds->ss;
453  salen = sizeof(struct sockaddr_in);
454  break;
455 
456  case AF_INET6:
457  if (NULL == rs->dnsout6)
458  rs->dnsout6 = open_socket(AF_INET6);
459  dnsout = rs->dnsout6;
460  sa = (const struct sockaddr *)&ds->ss;
461  salen = sizeof(struct sockaddr_in6);
462  break;
463 
464  default:
465  return;
466  }
467  if (NULL == dnsout)
468  {
470  "Unable to use configure DNS server, skipping\n");
471  return;
472  }
474  rs->request,
475  rs->request_len,
476  sa,
477  salen))
479  _("Failed to send DNS request to %s: %s\n"),
480  GNUNET_a2s(sa, salen),
481  strerror(errno));
482  else
484  _("Sent DNS request to %s\n"),
485  GNUNET_a2s(sa, salen));
486  schedule_read(rs);
487 }
488 
489 
502  const void *request,
503  size_t request_len,
505  void *rc_cls)
506 {
507  struct GNUNET_DNSSTUB_RequestSocket *rs;
508 
509  if (NULL == ctx->dns_head)
510  {
512  "No DNS server configured for resolution\n");
513  return NULL;
514  }
515  if (NULL == (rs = get_request_socket(ctx)))
516  {
518  "No request socket available for DNS resolution\n");
519  return NULL;
520  }
521  rs->ds_pos = ctx->dns_head;
522  rs->rc = rc;
523  rs->rc_cls = rc_cls;
524  rs->request = GNUNET_memdup(request, request_len);
525  rs->request_len = request_len;
527  return rs;
528 }
529 
530 
536 void
538 {
539  rs->rc = NULL;
540  if (NULL != rs->retry_task)
541  {
543  rs->retry_task = NULL;
544  }
545  if (NULL != rs->read_task)
546  {
548  rs->read_task = NULL;
549  }
550 }
551 
552 
560 struct GNUNET_DNSSTUB_Context *
562 {
563  struct GNUNET_DNSSTUB_Context *ctx;
564 
565  if (0 == num_sockets)
566  {
567  GNUNET_break(0);
568  return NULL;
569  }
570  ctx = GNUNET_new(struct GNUNET_DNSSTUB_Context);
571  ctx->num_sockets = num_sockets;
572  ctx->sockets =
573  GNUNET_new_array(num_sockets, struct GNUNET_DNSSTUB_RequestSocket);
575  return ctx;
576 }
577 
578 
587 int
589  const char *dns_ip)
590 {
591  struct DnsServer *ds;
592  struct in_addr i4;
593  struct in6_addr i6;
594 
595  ds = GNUNET_new(struct DnsServer);
596  if (1 == inet_pton(AF_INET, dns_ip, &i4))
597  {
598  struct sockaddr_in *s4 = (struct sockaddr_in *)&ds->ss;
599 
600  s4->sin_family = AF_INET;
601  s4->sin_port = htons(53);
602  s4->sin_addr = i4;
603 #if HAVE_SOCKADDR_IN_SIN_LEN
604  s4->sin_len = (u_char)sizeof(struct sockaddr_in);
605 #endif
606  }
607  else if (1 == inet_pton(AF_INET6, dns_ip, &i6))
608  {
609  struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&ds->ss;
610 
611  s6->sin6_family = AF_INET6;
612  s6->sin6_port = htons(53);
613  s6->sin6_addr = i6;
614 #if HAVE_SOCKADDR_IN_SIN_LEN
615  s6->sin6_len = (u_char)sizeof(struct sockaddr_in6);
616 #endif
617  }
618  else
619  {
621  "Malformed IP address `%s' for DNS server\n",
622  dns_ip);
623  GNUNET_free(ds);
624  return GNUNET_SYSERR;
625  }
627  return GNUNET_OK;
628 }
629 
630 
639 int
641  const struct sockaddr *sa)
642 {
643  struct DnsServer *ds;
644 
645  ds = GNUNET_new(struct DnsServer);
646  switch (sa->sa_family)
647  {
648  case AF_INET:
649  GNUNET_memcpy(&ds->ss, sa, sizeof(struct sockaddr_in));
650  break;
651 
652  case AF_INET6:
653  GNUNET_memcpy(&ds->ss, sa, sizeof(struct sockaddr_in6));
654  break;
655 
656  default:
657  GNUNET_break(0);
658  GNUNET_free(ds);
659  return GNUNET_SYSERR;
660  }
662  return GNUNET_OK;
663 }
664 
665 
673 void
675  struct GNUNET_TIME_Relative retry_freq)
676 {
677  ctx->retry_freq = retry_freq;
678 }
679 
680 
686 void
688 {
689  struct DnsServer *ds;
690 
691  while (NULL != (ds = ctx->dns_head))
692  {
694  GNUNET_free(ds);
695  }
696  for (unsigned int i = 0; i < ctx->num_sockets; i++)
697  cleanup_rs(&ctx->sockets[i]);
698  GNUNET_free(ctx->sockets);
699  GNUNET_free(ctx);
700 }
701 
702 
703 /* end of dnsstub.c */
#define GNUNET_CONTAINER_DLL_remove(head, tail, element)
Remove an element from a DLL.
const struct GNUNET_SCHEDULER_TaskContext * GNUNET_SCHEDULER_get_task_context(void)
Obtain the reasoning why the current task was started.
Definition: scheduler.c:737
int GNUNET_NETWORK_get_fd(const struct GNUNET_NETWORK_Handle *desc)
Return file descriptor for this network handle.
Definition: network.c:1068
struct GNUNET_SCHEDULER_Task * read_task
Task for reading from dnsout4 and dnsout6.
Definition: dnsstub.c:68
struct DnsServer * prev
Kept in a DLL.
Definition: dnsstub.c:109
void GNUNET_DNSSTUB_set_retry(struct GNUNET_DNSSTUB_Context *ctx, struct GNUNET_TIME_Relative retry_freq)
How long should we try requests before timing out? Only effective for requests issued after this call...
Definition: dnsstub.c:674
struct GNUNET_NETWORK_Handle * dnsout6
UDP socket we use for this request for IPv6.
Definition: dnsstub.c:53
void(* GNUNET_DNSSTUB_ResultCallback)(void *cls, const struct GNUNET_TUN_DnsHeader *dns, size_t dns_len)
Function called with the result of a DNS resolution.
struct DnsServer * dns_head
DLL of DNS resolvers we use.
Definition: dnsstub.c:130
#define GNUNET_CONTAINER_DLL_insert(head, tail, element)
Insert an element at the head of a DLL.
static void transmit_query(void *cls)
Task to (re)transmit the DNS query, possibly repeatedly until we succeed.
Definition: dnsstub.c:429
Context information passed to each scheduler task.
static char * dns_ip
IP of DNS server.
uint32_t GNUNET_CRYPTO_random_u32(enum GNUNET_CRYPTO_Quality mode, uint32_t i)
Produce a random value.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
void GNUNET_DNSSTUB_stop(struct GNUNET_DNSSTUB_Context *ctx)
Cleanup DNSSTUB resolver.
Definition: dnsstub.c:687
static struct GNUNET_SCHEDULER_TaskContext tc
Task context of the current task.
Definition: scheduler.c:410
GNUNET_DNSSTUB_ResultCallback rc
Function to call with result.
Definition: dnsstub.c:58
int GNUNET_DNSSTUB_add_dns_ip(struct GNUNET_DNSSTUB_Context *ctx, const char *dns_ip)
Add nameserver for use by the DNSSTUB.
Definition: dnsstub.c:588
int GNUNET_NETWORK_socket_bind(struct GNUNET_NETWORK_Handle *desc, const struct sockaddr *address, socklen_t address_len)
Bind a socket to a particular address.
Definition: network.c:474
#define DNS_RETRANSMIT_DELAY
Timeout for retrying DNS queries.
Definition: dnsstub.c:31
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
void * request
Query we sent to addr.
Definition: dnsstub.c:88
#define GNUNET_NO
Definition: gnunet_common.h:78
#define GNUNET_memdup(buf, size)
Allocate and initialize a block of memory.
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
const struct GNUNET_NETWORK_FDSet * read_ready
Set of file descriptors ready for reading; note that additional bits may be set that were not in the ...
#define GNUNET_new(type)
Allocate a struct or union of the given type.
size_t request_len
Number of bytes in request.
Definition: dnsstub.c:93
static int ret
Final status code.
Definition: gnunet-arm.c:89
struct sockaddr_storage ss
IP address of the DNS resolver.
Definition: dnsstub.c:114
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct GNUNET_DNSSTUB_Context * ctx
Context this request executes in.
Definition: dnsstub.c:83
struct DnsServer * next
Kept in a DLL.
Definition: dnsstub.c:104
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
void GNUNET_NETWORK_fdset_destroy(struct GNUNET_NETWORK_FDSet *fds)
Releases the associated memory of an fd set.
Definition: network.c:1254
#define GNUNET_log_strerror(level, cmd)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
static void read_response(void *cls)
Read a DNS response from the (unhindered) UDP-Socket.
Definition: dnsstub.c:401
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition: scheduler.c:1237
struct GNUNET_NETWORK_FDSet * GNUNET_NETWORK_fdset_create(void)
Creates an fd set.
Definition: network.c:1238
unsigned int num_sockets
Length of sockets array.
Definition: dnsstub.c:145
struct DnsServer * ds_pos
Next address we sent the DNS request to.
Definition: dnsstub.c:78
struct GNUNET_DNSSTUB_Context * GNUNET_DNSSTUB_start(unsigned int num_sockets)
Start a DNS stub resolver.
Definition: dnsstub.c:561
static struct GNUNET_NETWORK_Handle * open_socket(int af)
Open source port for sending DNS requests.
Definition: dnsstub.c:192
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run as soon as possible.
Definition: scheduler.c:1264
DNS Server used for resolution.
Definition: dnsstub.c:100
#define GNUNET_MIN(a, b)
Definition: gnunet_common.h:80
struct DnsServer * dns_tail
DLL of DNS resolvers we use.
Definition: dnsstub.c:135
ssize_t GNUNET_NETWORK_socket_sendto(const struct GNUNET_NETWORK_Handle *desc, const void *message, size_t length, const struct sockaddr *dest_addr, socklen_t dest_len)
Send data to a particular destination (always non-blocking).
Definition: network.c:838
Randomness for IVs etc.
collection of IO descriptors
static char buf[2048]
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_select(enum GNUNET_SCHEDULER_Priority prio, struct GNUNET_TIME_Relative delay, const struct GNUNET_NETWORK_FDSet *rs, const struct GNUNET_NETWORK_FDSet *ws, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay or when any of the specified file descriptor set...
Definition: scheduler.c:1784
#define GNUNET_TIME_UNIT_FOREVER_REL
Constant used to specify "forever".
const char * GNUNET_a2s(const struct sockaddr *addr, socklen_t addrlen)
Convert a "struct sockaddr*" (IPv4 or IPv6 address) to a string (for printing debug messages)...
struct GNUNET_DNSSTUB_RequestSocket * sockets
Array of all open sockets for DNS requests.
Definition: dnsstub.c:125
static struct GNUNET_DNSSTUB_RequestSocket * get_request_socket(struct GNUNET_DNSSTUB_Context *ctx)
Get a socket of the specified address family to send out a UDP DNS request to the Internet...
Definition: dnsstub.c:241
static int do_dns_read(struct GNUNET_DNSSTUB_RequestSocket *rs, struct GNUNET_NETWORK_Handle *dnsout)
Actually do the reading of a DNS packet from our UDP socket and see if we have a valid, matching, pending request.
Definition: dnsstub.c:287
Handle to the stub resolver.
Definition: dnsstub.c:121
void * rc_cls
Closure for rc.
Definition: dnsstub.c:63
struct GNUNET_DNSSTUB_RequestSocket * GNUNET_DNSSTUB_resolve(struct GNUNET_DNSSTUB_Context *ctx, const void *request, size_t request_len, GNUNET_DNSSTUB_ResultCallback rc, void *rc_cls)
Perform DNS resolution using our default IP from init.
Definition: dnsstub.c:501
static void schedule_read(struct GNUNET_DNSSTUB_RequestSocket *rs)
Schedule read_response() task for rs.
Definition: dnsstub.c:373
#define GNUNET_SYSERR
Definition: gnunet_common.h:76
void GNUNET_NETWORK_fdset_set(struct GNUNET_NETWORK_FDSet *fds, const struct GNUNET_NETWORK_Handle *desc)
Add a socket to the FD set.
Definition: network.c:999
struct GNUNET_NETWORK_Handle * dnsout4
UDP socket we use for this request for IPv4.
Definition: dnsstub.c:48
#define GNUNET_ALIGN
gcc-ism to force alignment; we use this to align char-arrays that may then be cast to &#39;struct&#39;s...
void GNUNET_DNSSTUB_resolve_cancel(struct GNUNET_DNSSTUB_RequestSocket *rs)
Cancel DNS resolution.
Definition: dnsstub.c:537
#define GNUNET_log(kind,...)
Entry in list of pending tasks.
Definition: scheduler.c:131
struct GNUNET_SCHEDULER_Task * retry_task
Task for retrying transmission of the query.
Definition: dnsstub.c:73
handle to a socket
Definition: network.c:46
int af
Address family / domain.
Definition: network.c:52
struct GNUNET_TIME_Relative retry_freq
How frequently do we retry requests?
Definition: dnsstub.c:140
#define GNUNET_YES
Definition: gnunet_common.h:77
static struct GNUNET_FS_DirScanner * ds
Handle to the directory scanner (for recursive insertions).
static void cleanup_rs(struct GNUNET_DNSSTUB_RequestSocket *rs)
We&#39;re done with a struct GNUNET_DNSSTUB_RequestSocket, close it for now.
Definition: dnsstub.c:155
Run with the default priority (normal P2P operations).
UDP socket we are using for sending DNS requests to the Internet.
Definition: dnsstub.c:44
int GNUNET_NETWORK_fdset_isset(const struct GNUNET_NETWORK_FDSet *fds, const struct GNUNET_NETWORK_Handle *desc)
Check whether a socket is part of the fd set.
Definition: network.c:1017
ssize_t GNUNET_NETWORK_socket_recvfrom(const struct GNUNET_NETWORK_Handle *desc, void *buffer, size_t length, struct sockaddr *src_addr, socklen_t *addrlen)
Read data from a socket (always non-blocking).
Definition: network.c:742
int GNUNET_DNSSTUB_add_dns_sa(struct GNUNET_DNSSTUB_Context *ctx, const struct sockaddr *sa)
Add nameserver for use by the DNSSTUB.
Definition: dnsstub.c:640
int GNUNET_NETWORK_socket_close(struct GNUNET_NETWORK_Handle *desc)
Close a socket.
Definition: network.c:548
#define GNUNET_free(ptr)
Wrapper around free.
Time for relative time used by GNUnet, in microseconds.
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
struct GNUNET_NETWORK_Handle * GNUNET_NETWORK_socket_create(int domain, int type, int protocol)
Create a new socket.
Definition: network.c:900
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition: scheduler.c:956