]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-answer.c
Merge pull request #8025 from sourcejedi/pid1_journal_or2
[thirdparty/systemd.git] / src / resolve / resolved-dns-answer.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "alloc-util.h"
22 #include "dns-domain.h"
23 #include "resolved-dns-answer.h"
24 #include "resolved-dns-dnssec.h"
25 #include "string-util.h"
26
27 DnsAnswer *dns_answer_new(unsigned n) {
28 DnsAnswer *a;
29
30 a = malloc0(offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * n);
31 if (!a)
32 return NULL;
33
34 a->n_ref = 1;
35 a->n_allocated = n;
36
37 return a;
38 }
39
40 DnsAnswer *dns_answer_ref(DnsAnswer *a) {
41 if (!a)
42 return NULL;
43
44 assert(a->n_ref > 0);
45 a->n_ref++;
46 return a;
47 }
48
49 static void dns_answer_flush(DnsAnswer *a) {
50 DnsResourceRecord *rr;
51
52 if (!a)
53 return;
54
55 DNS_ANSWER_FOREACH(rr, a)
56 dns_resource_record_unref(rr);
57
58 a->n_rrs = 0;
59 }
60
61 DnsAnswer *dns_answer_unref(DnsAnswer *a) {
62 if (!a)
63 return NULL;
64
65 assert(a->n_ref > 0);
66
67 if (a->n_ref == 1) {
68 dns_answer_flush(a);
69 free(a);
70 } else
71 a->n_ref--;
72
73 return NULL;
74 }
75
76 static int dns_answer_add_raw(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
77 assert(rr);
78
79 if (!a)
80 return -ENOSPC;
81
82 if (a->n_rrs >= a->n_allocated)
83 return -ENOSPC;
84
85 a->items[a->n_rrs++] = (DnsAnswerItem) {
86 .rr = dns_resource_record_ref(rr),
87 .ifindex = ifindex,
88 .flags = flags,
89 };
90
91 return 1;
92 }
93
94 static int dns_answer_add_raw_all(DnsAnswer *a, DnsAnswer *source) {
95 DnsResourceRecord *rr;
96 DnsAnswerFlags flags;
97 int ifindex, r;
98
99 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, source) {
100 r = dns_answer_add_raw(a, rr, ifindex, flags);
101 if (r < 0)
102 return r;
103 }
104
105 return 0;
106 }
107
108 int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
109 unsigned i;
110 int r;
111
112 assert(rr);
113
114 if (!a)
115 return -ENOSPC;
116 if (a->n_ref > 1)
117 return -EBUSY;
118
119 for (i = 0; i < a->n_rrs; i++) {
120 if (a->items[i].ifindex != ifindex)
121 continue;
122
123 r = dns_resource_record_equal(a->items[i].rr, rr);
124 if (r < 0)
125 return r;
126 if (r > 0) {
127 /* Don't mix contradicting TTLs (see below) */
128 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
129 return -EINVAL;
130
131 /* Entry already exists, keep the entry with
132 * the higher RR. */
133 if (rr->ttl > a->items[i].rr->ttl) {
134 dns_resource_record_ref(rr);
135 dns_resource_record_unref(a->items[i].rr);
136 a->items[i].rr = rr;
137 }
138
139 a->items[i].flags |= flags;
140 return 0;
141 }
142
143 r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
144 if (r < 0)
145 return r;
146 if (r > 0) {
147 /* There's already an RR of the same RRset in
148 * place! Let's see if the TTLs more or less
149 * match. We don't really care if they match
150 * precisely, but we do care whether one is 0
151 * and the other is not. See RFC 2181, Section
152 * 5.2. */
153
154 if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
155 return -EINVAL;
156 }
157 }
158
159 return dns_answer_add_raw(a, rr, ifindex, flags);
160 }
161
162 static int dns_answer_add_all(DnsAnswer *a, DnsAnswer *b) {
163 DnsResourceRecord *rr;
164 DnsAnswerFlags flags;
165 int ifindex, r;
166
167 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, b) {
168 r = dns_answer_add(a, rr, ifindex, flags);
169 if (r < 0)
170 return r;
171 }
172
173 return 0;
174 }
175
176 int dns_answer_add_extend(DnsAnswer **a, DnsResourceRecord *rr, int ifindex, DnsAnswerFlags flags) {
177 int r;
178
179 assert(a);
180 assert(rr);
181
182 r = dns_answer_reserve_or_clone(a, 1);
183 if (r < 0)
184 return r;
185
186 return dns_answer_add(*a, rr, ifindex, flags);
187 }
188
189 int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex) {
190 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *soa = NULL;
191
192 soa = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_SOA, name);
193 if (!soa)
194 return -ENOMEM;
195
196 soa->ttl = ttl;
197
198 soa->soa.mname = strdup(name);
199 if (!soa->soa.mname)
200 return -ENOMEM;
201
202 soa->soa.rname = strappend("root.", name);
203 if (!soa->soa.rname)
204 return -ENOMEM;
205
206 soa->soa.serial = 1;
207 soa->soa.refresh = 1;
208 soa->soa.retry = 1;
209 soa->soa.expire = 1;
210 soa->soa.minimum = ttl;
211
212 return dns_answer_add(a, soa, ifindex, DNS_ANSWER_AUTHENTICATED);
213 }
214
215 int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
216 DnsAnswerFlags flags = 0, i_flags;
217 DnsResourceRecord *i;
218 bool found = false;
219 int r;
220
221 assert(key);
222
223 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
224 r = dns_resource_key_match_rr(key, i, NULL);
225 if (r < 0)
226 return r;
227 if (r == 0)
228 continue;
229
230 if (!ret_flags)
231 return 1;
232
233 if (found)
234 flags &= i_flags;
235 else {
236 flags = i_flags;
237 found = true;
238 }
239 }
240
241 if (ret_flags)
242 *ret_flags = flags;
243
244 return found;
245 }
246
247 int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *ret_flags) {
248 DnsAnswerFlags flags = 0, i_flags;
249 DnsResourceRecord *i;
250 bool found = false;
251 int r;
252
253 assert(rr);
254
255 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
256 r = dns_resource_record_equal(i, rr);
257 if (r < 0)
258 return r;
259 if (r == 0)
260 continue;
261
262 if (!ret_flags)
263 return 1;
264
265 if (found)
266 flags &= i_flags;
267 else {
268 flags = i_flags;
269 found = true;
270 }
271 }
272
273 if (ret_flags)
274 *ret_flags = flags;
275
276 return found;
277 }
278
279 int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *ret_flags) {
280 DnsAnswerFlags flags = 0, i_flags;
281 DnsResourceRecord *i;
282 bool found = false;
283 int r;
284
285 assert(key);
286
287 DNS_ANSWER_FOREACH_FLAGS(i, i_flags, a) {
288 r = dns_resource_key_equal(i->key, key);
289 if (r < 0)
290 return r;
291 if (r == 0)
292 continue;
293
294 if (!ret_flags)
295 return true;
296
297 if (found)
298 flags &= i_flags;
299 else {
300 flags = i_flags;
301 found = true;
302 }
303 }
304
305 if (ret_flags)
306 *ret_flags = flags;
307
308 return found;
309 }
310
311 int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
312 DnsResourceRecord *i;
313
314 DNS_ANSWER_FOREACH(i, a) {
315 if (IN_SET(i->key->type, DNS_TYPE_NSEC, DNS_TYPE_NSEC3))
316 return true;
317 }
318
319 return false;
320 }
321
322 int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
323 DnsResourceRecord *rr;
324 int r;
325
326 /* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
327
328 DNS_ANSWER_FOREACH(rr, answer) {
329 const char *p;
330
331 if (rr->key->type != DNS_TYPE_NSEC3)
332 continue;
333
334 p = dns_resource_key_name(rr->key);
335 r = dns_name_parent(&p);
336 if (r < 0)
337 return r;
338 if (r == 0)
339 continue;
340
341 r = dns_name_equal(p, zone);
342 if (r != 0)
343 return r;
344 }
345
346 return false;
347 }
348
349 int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
350 DnsResourceRecord *rr, *soa = NULL;
351 DnsAnswerFlags rr_flags, soa_flags = 0;
352 int r;
353
354 assert(key);
355
356 /* For a SOA record we can never find a matching SOA record */
357 if (key->type == DNS_TYPE_SOA)
358 return 0;
359
360 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
361 r = dns_resource_key_match_soa(key, rr->key);
362 if (r < 0)
363 return r;
364 if (r > 0) {
365
366 if (soa) {
367 r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key));
368 if (r < 0)
369 return r;
370 if (r > 0)
371 continue;
372 }
373
374 soa = rr;
375 soa_flags = rr_flags;
376 }
377 }
378
379 if (!soa)
380 return 0;
381
382 if (ret)
383 *ret = soa;
384 if (flags)
385 *flags = soa_flags;
386
387 return 1;
388 }
389
390 int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
391 DnsResourceRecord *rr;
392 DnsAnswerFlags rr_flags;
393 int r;
394
395 assert(key);
396
397 /* For a {C,D}NAME record we can never find a matching {C,D}NAME record */
398 if (!dns_type_may_redirect(key->type))
399 return 0;
400
401 DNS_ANSWER_FOREACH_FLAGS(rr, rr_flags, a) {
402 r = dns_resource_key_match_cname_or_dname(key, rr->key, NULL);
403 if (r < 0)
404 return r;
405 if (r > 0) {
406 if (ret)
407 *ret = rr;
408 if (flags)
409 *flags = rr_flags;
410 return 1;
411 }
412 }
413
414 return 0;
415 }
416
417 int dns_answer_merge(DnsAnswer *a, DnsAnswer *b, DnsAnswer **ret) {
418 _cleanup_(dns_answer_unrefp) DnsAnswer *k = NULL;
419 int r;
420
421 assert(ret);
422
423 if (dns_answer_size(a) <= 0) {
424 *ret = dns_answer_ref(b);
425 return 0;
426 }
427
428 if (dns_answer_size(b) <= 0) {
429 *ret = dns_answer_ref(a);
430 return 0;
431 }
432
433 k = dns_answer_new(a->n_rrs + b->n_rrs);
434 if (!k)
435 return -ENOMEM;
436
437 r = dns_answer_add_raw_all(k, a);
438 if (r < 0)
439 return r;
440
441 r = dns_answer_add_all(k, b);
442 if (r < 0)
443 return r;
444
445 *ret = k;
446 k = NULL;
447
448 return 0;
449 }
450
451 int dns_answer_extend(DnsAnswer **a, DnsAnswer *b) {
452 DnsAnswer *merged;
453 int r;
454
455 assert(a);
456
457 r = dns_answer_merge(*a, b, &merged);
458 if (r < 0)
459 return r;
460
461 dns_answer_unref(*a);
462 *a = merged;
463
464 return 0;
465 }
466
467 int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) {
468 bool found = false, other = false;
469 DnsResourceRecord *rr;
470 unsigned i;
471 int r;
472
473 assert(a);
474 assert(key);
475
476 /* Remove all entries matching the specified key from *a */
477
478 DNS_ANSWER_FOREACH(rr, *a) {
479 r = dns_resource_key_equal(rr->key, key);
480 if (r < 0)
481 return r;
482 if (r > 0)
483 found = true;
484 else
485 other = true;
486
487 if (found && other)
488 break;
489 }
490
491 if (!found)
492 return 0;
493
494 if (!other) {
495 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
496 return 1;
497 }
498
499 if ((*a)->n_ref > 1) {
500 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
501 DnsAnswerFlags flags;
502 int ifindex;
503
504 copy = dns_answer_new((*a)->n_rrs);
505 if (!copy)
506 return -ENOMEM;
507
508 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
509 r = dns_resource_key_equal(rr->key, key);
510 if (r < 0)
511 return r;
512 if (r > 0)
513 continue;
514
515 r = dns_answer_add_raw(copy, rr, ifindex, flags);
516 if (r < 0)
517 return r;
518 }
519
520 dns_answer_unref(*a);
521 *a = copy;
522 copy = NULL;
523
524 return 1;
525 }
526
527 /* Only a single reference, edit in-place */
528
529 i = 0;
530 for (;;) {
531 if (i >= (*a)->n_rrs)
532 break;
533
534 r = dns_resource_key_equal((*a)->items[i].rr->key, key);
535 if (r < 0)
536 return r;
537 if (r > 0) {
538 /* Kill this entry */
539
540 dns_resource_record_unref((*a)->items[i].rr);
541 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
542 (*a)->n_rrs--;
543 continue;
544
545 } else
546 /* Keep this entry */
547 i++;
548 }
549
550 return 1;
551 }
552
553 int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) {
554 bool found = false, other = false;
555 DnsResourceRecord *rr;
556 unsigned i;
557 int r;
558
559 assert(a);
560 assert(rm);
561
562 /* Remove all entries matching the specified RR from *a */
563
564 DNS_ANSWER_FOREACH(rr, *a) {
565 r = dns_resource_record_equal(rr, rm);
566 if (r < 0)
567 return r;
568 if (r > 0)
569 found = true;
570 else
571 other = true;
572
573 if (found && other)
574 break;
575 }
576
577 if (!found)
578 return 0;
579
580 if (!other) {
581 *a = dns_answer_unref(*a); /* Return NULL for the empty answer */
582 return 1;
583 }
584
585 if ((*a)->n_ref > 1) {
586 _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL;
587 DnsAnswerFlags flags;
588 int ifindex;
589
590 copy = dns_answer_new((*a)->n_rrs);
591 if (!copy)
592 return -ENOMEM;
593
594 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) {
595 r = dns_resource_record_equal(rr, rm);
596 if (r < 0)
597 return r;
598 if (r > 0)
599 continue;
600
601 r = dns_answer_add_raw(copy, rr, ifindex, flags);
602 if (r < 0)
603 return r;
604 }
605
606 dns_answer_unref(*a);
607 *a = copy;
608 copy = NULL;
609
610 return 1;
611 }
612
613 /* Only a single reference, edit in-place */
614
615 i = 0;
616 for (;;) {
617 if (i >= (*a)->n_rrs)
618 break;
619
620 r = dns_resource_record_equal((*a)->items[i].rr, rm);
621 if (r < 0)
622 return r;
623 if (r > 0) {
624 /* Kill this entry */
625
626 dns_resource_record_unref((*a)->items[i].rr);
627 memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1));
628 (*a)->n_rrs--;
629 continue;
630
631 } else
632 /* Keep this entry */
633 i++;
634 }
635
636 return 1;
637 }
638
639 int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
640 DnsResourceRecord *rr_source;
641 int ifindex_source, r;
642 DnsAnswerFlags flags_source;
643
644 assert(a);
645 assert(key);
646
647 /* Copy all RRs matching the specified key from source into *a */
648
649 DNS_ANSWER_FOREACH_FULL(rr_source, ifindex_source, flags_source, source) {
650
651 r = dns_resource_key_equal(rr_source->key, key);
652 if (r < 0)
653 return r;
654 if (r == 0)
655 continue;
656
657 /* Make space for at least one entry */
658 r = dns_answer_reserve_or_clone(a, 1);
659 if (r < 0)
660 return r;
661
662 r = dns_answer_add(*a, rr_source, ifindex_source, flags_source|or_flags);
663 if (r < 0)
664 return r;
665 }
666
667 return 0;
668 }
669
670 int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags) {
671 int r;
672
673 assert(to);
674 assert(from);
675 assert(key);
676
677 r = dns_answer_copy_by_key(to, *from, key, or_flags);
678 if (r < 0)
679 return r;
680
681 return dns_answer_remove_by_key(from, key);
682 }
683
684 void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
685 DnsAnswerItem *items;
686 unsigned i, start, end;
687
688 if (!a)
689 return;
690
691 if (a->n_rrs <= 1)
692 return;
693
694 start = 0;
695 end = a->n_rrs-1;
696
697 /* RFC 4795, Section 2.6 suggests we should order entries
698 * depending on whether the sender is a link-local address. */
699
700 items = newa(DnsAnswerItem, a->n_rrs);
701 for (i = 0; i < a->n_rrs; i++) {
702
703 if (a->items[i].rr->key->class == DNS_CLASS_IN &&
704 ((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
705 (a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
706 /* Order address records that are not preferred to the end of the array */
707 items[end--] = a->items[i];
708 else
709 /* Order all other records to the beginning of the array */
710 items[start++] = a->items[i];
711 }
712
713 assert(start == end+1);
714 memcpy(a->items, items, sizeof(DnsAnswerItem) * a->n_rrs);
715 }
716
717 int dns_answer_reserve(DnsAnswer **a, unsigned n_free) {
718 DnsAnswer *n;
719
720 assert(a);
721
722 if (n_free <= 0)
723 return 0;
724
725 if (*a) {
726 unsigned ns;
727
728 if ((*a)->n_ref > 1)
729 return -EBUSY;
730
731 ns = (*a)->n_rrs + n_free;
732
733 if ((*a)->n_allocated >= ns)
734 return 0;
735
736 /* Allocate more than we need */
737 ns *= 2;
738
739 n = realloc(*a, offsetof(DnsAnswer, items) + sizeof(DnsAnswerItem) * ns);
740 if (!n)
741 return -ENOMEM;
742
743 n->n_allocated = ns;
744 } else {
745 n = dns_answer_new(n_free);
746 if (!n)
747 return -ENOMEM;
748 }
749
750 *a = n;
751 return 0;
752 }
753
754 int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) {
755 _cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
756 int r;
757
758 assert(a);
759
760 /* Tries to extend the DnsAnswer object. And if that's not
761 * possible, since we are not the sole owner, then allocate a
762 * new, appropriately sized one. Either way, after this call
763 * the object will only have a single reference, and has room
764 * for at least the specified number of RRs. */
765
766 r = dns_answer_reserve(a, n_free);
767 if (r != -EBUSY)
768 return r;
769
770 assert(*a);
771
772 n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
773 if (!n)
774 return -ENOMEM;
775
776 r = dns_answer_add_raw_all(n, *a);
777 if (r < 0)
778 return r;
779
780 dns_answer_unref(*a);
781 *a = n;
782 n = NULL;
783
784 return 0;
785 }
786
787 void dns_answer_dump(DnsAnswer *answer, FILE *f) {
788 DnsResourceRecord *rr;
789 DnsAnswerFlags flags;
790 int ifindex;
791
792 if (!f)
793 f = stdout;
794
795 DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) {
796 const char *t;
797
798 fputc('\t', f);
799
800 t = dns_resource_record_to_string(rr);
801 if (!t) {
802 log_oom();
803 continue;
804 }
805
806 fputs(t, f);
807
808 if (ifindex != 0 || flags & (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE|DNS_ANSWER_SHARED_OWNER))
809 fputs("\t;", f);
810
811 if (ifindex != 0)
812 printf(" ifindex=%i", ifindex);
813 if (flags & DNS_ANSWER_AUTHENTICATED)
814 fputs(" authenticated", f);
815 if (flags & DNS_ANSWER_CACHEABLE)
816 fputs(" cachable", f);
817 if (flags & DNS_ANSWER_SHARED_OWNER)
818 fputs(" shared-owner", f);
819
820 fputc('\n', f);
821 }
822 }
823
824 bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
825 DnsResourceRecord *rr;
826 int r;
827
828 assert(cname);
829
830 /* Checks whether the answer contains a DNAME record that indicates that the specified CNAME record is
831 * synthesized from it */
832
833 if (cname->key->type != DNS_TYPE_CNAME)
834 return 0;
835
836 DNS_ANSWER_FOREACH(rr, a) {
837 _cleanup_free_ char *n = NULL;
838
839 if (rr->key->type != DNS_TYPE_DNAME)
840 continue;
841 if (rr->key->class != cname->key->class)
842 continue;
843
844 r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n);
845 if (r < 0)
846 return r;
847 if (r == 0)
848 continue;
849
850 r = dns_name_equal(n, dns_resource_key_name(cname->key));
851 if (r < 0)
852 return r;
853 if (r > 0)
854 return 1;
855
856 }
857
858 return 0;
859 }