aboutsummaryrefslogtreecommitdiff
path: root/route.c
blob: 5d8f8d66afc2fe0f58ac44da5885075e87b8e32e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
# XZ Utils German translation
# This file is put in the public domain.
# Andre Noll <maan@tuebingen.mpg.de>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: XZ Utils 4.999.9beta\n"
"Report-Msgid-Bugs-To: lasse.collin@tukaani.org\n"
"POT-Creation-Date: 2015-08-07 05:10+0200\n"
"PO-Revision-Date: 2015-08-07 14:00+0200\n"
"Last-Translator:  <sqrt@entless.org>\n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: src/xz/args.c:63
#, c-format
msgid "%s: Invalid argument to --block-list"
msgstr "%s: Ungültiges Argument für --block-list"

#: src/xz/args.c:73
#, c-format
msgid "%s: Too many arguments to --block-list"
msgstr "%s: Zu viele Argumente für --block-list"

#: src/xz/args.c:102
msgid "0 can only be used as the last element in --block-list"
msgstr "0 kann nur das letzte Element in --block-list sein"

#: src/xz/args.c:406
#, c-format
msgid "%s: Unknown file format type"
msgstr "%s: Unbekanntes Dateiformat"

#: src/xz/args.c:429 src/xz/args.c:437
#, c-format
msgid "%s: Unsupported integrity check type"
msgstr "%s: Integritäts-Check-Typ nicht unterstützt"

#: src/xz/args.c:473
msgid "Only one file can be specified with `--files' or `--files0'."
msgstr "Nur eine Datei kann als Argument für --files oder --files0 angegeben werden."

#: src/xz/args.c:541
#, c-format
msgid "The environment variable %s contains too many arguments"
msgstr "Die Umgebungsvariable %s enthält zu viele Argumente"

#: src/xz/coder.c:110
msgid "Maximum number of filters is four"
msgstr "Maximal vier Filter möglich"

#: src/xz/coder.c:129
msgid "Memory usage limit is too low for the given filter setup."
msgstr "Das Speicher-Limit ist zu niedrig für die gegebene Filter-Konfiguration."

#: src/xz/coder.c:159
msgid "Using a preset in raw mode is discouraged."
msgstr "Verwendung einer Voreinstellung im Raw-Modus wird nicht empfohlen."

#: src/xz/coder.c:161
msgid "The exact options of the presets may vary between software versions."
msgstr "Die genauen Optionen der Voreinstellung können zwischen Softwareversionen variieren."

#: src/xz/coder.c:184
msgid "The .lzma format supports only the LZMA1 filter"
msgstr "Das .lzma-Format unterstützt nur den LZMA1-Filter"

#: src/xz/coder.c:192
msgid "LZMA1 cannot be used with the .xz format"
msgstr "LZMA1 kann nicht mit dem .xz-Format verwendet werden"

#: src/xz/coder.c:209
msgid "The filter chain is incompatible with --flush-timeout"
msgstr "Diese Filterkette ist inkompatibel zu --flush-timeout"

#: src/xz/coder.c:215
msgid "Switching to single-threaded mode due to --flush-timeout"
msgstr "Schalte um auf Single-Thread-Modus wegen --flush-timeout"

#: src/xz/coder.c:234
#, c-format
msgid "Using up to %<PRIu32> threads."
msgstr "Benutze bis zu %<PRIu32> Threads."

#: src/xz/coder.c:247
msgid "Unsupported filter chain or filter options"
msgstr "Optionen nicht unterstützt"

#: src/xz/coder.c:255
#, c-format
msgid "Decompression will need %s MiB of memory."
msgstr "Dekomprimierung wird %s MiB Speicher brauchen."

#: src/xz/coder.c:290
#, c-format
msgid "Adjusted the number of threads from %s to %s to not exceed the memory usage limit of %s MiB"
msgstr "Passte die Anzahl Threads von %s auf %s an um nicht das Speichernutzungslimit von %s MiB zu übersteigen"

#: src/xz/coder.c:344
#, c-format
msgid "Adjusted LZMA%c dictionary size from %s MiB to %s MiB to not exceed the memory usage limit of %s MiB"
msgstr "Passte LZMA%c-Wörterbuchgröße von %s MiB to %s MiB an, um nicht das Speichernutzungslimit von %s MiB zu übersteigen"

#: src/xz/file_io.c:100 src/xz/file_io.c:108
#, c-format
msgid "Error creating a pipe: %s"
msgstr "Fehler beim Erzeugen der Pipeline: %s"

#: src/xz/file_io.c:163
msgid "Sandbox is disabled due to incompatible command line arguments"
msgstr "Sandbox ist wegen inkompatibler Kommandozeilenargumente deaktiviert"

#: src/xz/file_io.c:206
msgid "Sandbox was successfully enabled"
msgstr "Sandbox wurde erfolgreich aktiviert"

#: src/xz/file_io.c:210
msgid "Failed to enable the sandbox"
msgstr "Konnte Sandbox nicht aktivieren"

#: src/xz/file_io.c:252
#, c-format
msgid "%s: poll() failed: %s"
msgstr "%s: poll() Fehler: %s"

#. TRANSLATORS: When compression or decompression finishes,
#. and xz is going to remove the source file, xz first checks
#. if the source file still exists, and if it does, does its
#. device and inode numbers match what xz saw when it opened
#. the source file. If these checks fail, this message is
#. shown, %s being the filename, and the file is not deleted.
#. The check for device and inode numbers is there, because
#. it is possible that the user has put a new file in place
#. of the original file, and in that case it obviously
#. shouldn't be removed.
#: src/xz/file_io.c:322
#, c-format
msgid "%s: File seems to have been moved, not removing"
msgstr "%s: Datei scheint umbenannt worden zu sein, daher wird sie nicht gelöscht"

#: src/xz/file_io.c:329 src/xz/file_io.c:847
#, c-format
msgid "%s: Cannot remove: %s"
msgstr "%s: Kann nicht löschen: %s"

#: src/xz/file_io.c:354
#, c-format
msgid "%s: Cannot set the file owner: %s"
msgstr "%s: Kann Dateieigentümer nicht setzen: %s"

#: src/xz/file_io.c:360
#, c-format
msgid "%s: Cannot set the file group: %s"
msgstr "%s: Kann Dateigruppe nicht setzen: %s"

#: src/xz/file_io.c:379
#, c-format
msgid "%s: Cannot set the file permissions: %s"
msgstr "%s: Kann Zugriffsrechte nicht setzen: %s"

#: src/xz/file_io.c:489
#, c-format
msgid "Error getting the file status flags from standard input: %s"
msgstr "Kann Status-Flags der Standardeingabe nicht ermitteln: %s"

#: src/xz/file_io.c:543 src/xz/file_io.c:605
#, c-format
msgid "%s: Is a symbolic link, skipping"
msgstr "%s: Überspringe symbolischen Link"

#: src/xz/file_io.c:634
#, c-format
msgid "%s: Is a directory, skipping"
msgstr "%s: Überspringe Verzeichnis"

#: src/xz/file_io.c:640
#, c-format
msgid "%s: Not a regular file, skipping"
msgstr "%s: Keine reguläre Datei, überspringe"

#: src/xz/file_io.c:657
#, c-format
msgid "%s: File has setuid or setgid bit set, skipping"
msgstr "%s: Datei hat das setuid- oder setgid-Bit gesetzt, überspringe"

#: src/xz/file_io.c:664
#, c-format
msgid "%s: File has sticky bit set, skipping"
msgstr "%s: Datei hat sticky-Bit gesetzt, überspringe"

#: src/xz/file_io.c:671
#, c-format
msgid "%s: Input file has more than one hard link, skipping"
msgstr "%s: Eingabedatei hat mehr als einen Hard Link, überspringe"

#: src/xz/file_io.c:756
#, c-format
msgid "Error restoring the status flags to standard input: %s"
msgstr "Fehler beim Wiederherstellen der Status-Flags für die Standardausgabe: %s"

#: src/xz/file_io.c:805
#, c-format
msgid "Error getting the file status flags from standard output: %s"
msgstr "Kann Status-Flags der Standardausgabe nicht ermitteln: %s"

#: src/xz/file_io.c:983
#, c-format
msgid "Error restoring the O_APPEND flag to standard output: %s"
msgstr "Fehler beim Wiederherstellen des O_APPEND-Flags bei Standardausgabe: %s"

#: src/xz/file_io.c:995
#, c-format
msgid "%s: Closing the file failed: %s"
msgstr "%s: Fehler beim Schließen der Datei: %s"

#: src/xz/file_io.c:1031 src/xz/file_io.c:1257
#, c-format
msgid "%s: Seeking failed when trying to create a sparse file: %s"
msgstr "%s: Positionierungsfehler beim Versuch eine sparse (dünnbesetzte) Datei zu erzeugen: %s"

#: src/xz/file_io.c:1126
#, c-format
msgid "%s: Read error: %s"
msgstr "%s: Lesefehler: %s"

#: src/xz/file_io.c:1146
#, c-format
msgid "%s: Error seeking the file: %s"
msgstr "%s: Fehler beim Lesen der Dateinamen: %s"

#: src/xz/file_io.c:1156
#, c-format
msgid "%s: Unexpected end of file"
msgstr "%s: Unerwartetes Ende der Datei"

#: src/xz/file_io.c:1215
#, c-format
msgid "%s: Write error: %s"
msgstr "%s: Schreibfehler: %s"

#: src/xz/hardware.c:107
msgid "Disabled"
msgstr "Deaktiviert"

#. TRANSLATORS: Test with "xz --info-memory" to see if
#. the alignment looks nice.
#: src/xz/hardware.c:126
msgid "Total amount of physical memory (RAM): "
msgstr "Gesamtmenge physikalischer Speicher (RAM):  "

#: src/xz/hardware.c:128
msgid "Memory usage limit for compression:    "
msgstr "Speichernutzungslimit für Komprimierung:    "

#: src/xz/hardware.c:130
msgid "Memory usage limit for decompression:  "
msgstr "Speichernutzungslimit für Dekomprimierung:  "

#. TRANSLATORS: Indicates that there is no integrity check.
#. This string is used in tables, so the width must not
#. exceed ten columns with a fixed-width font.
#: src/xz/list.c:65
msgid "None"
msgstr "Kein"

#. TRANSLATORS: Indicates that integrity check name is not known,
#. but the Check ID is known (here 2). This and other "Unknown-N"
#. strings are used in tables, so the width must not exceed ten
#. columns with a fixed-width font. It's OK to omit the dash if
#. you need space for one extra letter, but don't use spaces.
#: src/xz/list.c:72
msgid "Unknown-2"
msgstr "Unbek.2"

#: src/xz/list.c:73
msgid "Unknown-3"
msgstr "Unbek.3"

#: src/xz/list.c:75
msgid "Unknown-5"
msgstr "Unbek.5"

#: src/xz/list.c:76
msgid "Unknown-6"
msgstr "Unbek.6"

#: src/xz/list.c:77
msgid "Unknown-7"
msgstr "Unbek.7"

#: src/xz/list.c:78
msgid "Unknown-8"
msgstr "Unbek.8"

#: src/xz/list.c:79
msgid "Unknown-9"
msgstr "Unbek.9"

#: src/xz/list.c:81
msgid "Unknown-11"
msgstr "Unbek.11"

#: src/xz/list.c:82
msgid "Unknown-12"
msgstr "Unbek.12"

#: src/xz/list.c:83
msgid "Unknown-13"
msgstr "Unbek.13"

#: src/xz/list.c:84
msgid "Unknown-14"
msgstr "Unbek.14"

#: src/xz/list.c:85
msgid "Unknown-15"
msgstr "Unbek.15"

#: src/xz/list.c:153
#, c-format
msgid "%s: File is empty"
msgstr "%s: Datei ist leer"

#: src/xz/list.c:158
#, c-format
msgid "%s: Too small to be a valid .xz file"
msgstr "%s: Zu klein um eine gültige .xz-Datei zu sein"

#. TRANSLATORS: These are column headings. From Strms (Streams)
#. to Ratio, the columns are right aligned. Check and Filename
#. are left aligned. If you need longer words, it's OK to
#. use two lines here. Test with "xz -l foo.xz".
#: src/xz/list.c:671
msgid "Strms  Blocks   Compressed Uncompressed  Ratio  Check   Filename"
msgstr " Str.  Blöcke       Kompr.     Unkompr.  Verh.  Check   Dateiname"

#: src/xz/list.c:711
#, c-format
msgid "  Streams:            %s\n"
msgstr "  Ströme:              %s\n"

#: src/xz/list.c:713
#, c-format
msgid "  Blocks:             %s\n"
msgstr "  Blöcke:              %s\n"

#: src/xz/list.c:715
#, c-format
msgid "  Compressed size:    %s\n"
msgstr "  Größe komprimiert:   %s\n"

#: src/xz/list.c:718
#, c-format
msgid "  Uncompressed size:  %s\n"
msgstr "  Größe unkomprimiert: %s\n"

#: src/xz/list.c:721
#, c-format
msgid "  Ratio:              %s\n"
msgstr "  Verhältnis:          %s\n"

#: src/xz/list.c:723
#, c-format
msgid "  Check:              %s\n"
msgstr "  Check:               %s\n"

#: src/xz/list.c:724
#, c-format
msgid "  Stream padding:     %s\n"
msgstr "  Strom-Auffüllung:    %s\n"

#. TRANSLATORS: The second line is column headings. All except
#. Check are right aligned; Check is left aligned. Test with
#. "xz -lv foo.xz".
#: src/xz/list.c:752
msgid ""
"  Streams:\n"
"    Stream    Blocks      CompOffset    UncompOffset        CompSize      UncompSize  Ratio  Check      Padding"
msgstr ""
"  Ströme:\n"
"     Strom    Blöcke      KompOffset    UnkompOffset       KompGröße     UnkompGröße  Verh.  Check   Auffüllung"

#. TRANSLATORS: The second line is column headings. All
#. except Check are right aligned; Check is left aligned.
#: src/xz/list.c:807
#, c-format
msgid ""
"  Blocks:\n"
"    Stream     Block      CompOffset    UncompOffset       TotalSize      UncompSize  Ratio  Check"
msgstr ""
"  Blöcke:\n"
"     Strom     Block      KompOffset    UnkompOffset      TotalGröße     UnkompGröße  Verh.  Check"

#. TRANSLATORS: These are additional column headings
#. for the most verbose listing mode. CheckVal
#. (Check value), Flags, and Filters are left aligned.
#. Header (Block Header Size), CompSize, and MemUsage
#. are right aligned. %*s is replaced with 0-120
#. spaces to make the CheckVal column wide enough.
#. Test with "xz -lvv foo.xz".
#: src/xz/list.c:819
#, c-format
msgid "      CheckVal %*s Header  Flags        CompSize    MemUsage  Filters"
msgstr "      CheckWert %*s  Kopf  Schalter    KompGröße    Speicher  Filter"

#: src/xz/list.c:897 src/xz/list.c:1072
#, c-format
msgid "  Memory needed:      %s MiB\n"
msgstr "  Benötigter Speicher: %s MiB\n"

#: src/xz/list.c:899 src/xz/list.c:1074
#, c-format
msgid "  Sizes in headers:   %s\n"
msgstr "  Größe in Köpfen:     %s\n"

#: src/xz/list.c:900 src/xz/list.c:1075
msgid "Yes"
msgstr "Ja"

#: src/xz/list.c:900 src/xz/list.c:1075
msgid "No"
msgstr "Nein"

#: src/xz/list.c:901 src/xz/list.c:1076
#, c-format
msgid "  Minimum XZ Utils version: %s\n"
msgstr "  Kleinste XZ Utils-Version: %s\n"

#. TRANSLATORS: %s is an integer. Only the plural form of this
#. message is used (e.g. "2 files"). Test with "xz -l foo.xz bar.xz".
#: src/xz/list.c:1051
#, c-format
msgid "%s file\n"
msgid_plural "%s files\n"
msgstr[0] "%s Datei\n"
msgstr[1] "%s Dateien\n"

#: src/xz/list.c:1064
msgid "Totals:"
msgstr "Gesamt:"

#: src/xz/list.c:1065
#, c-format
msgid "  Number of files:    %s\n"
msgstr "   Anzahl Dateien:     %s\n"

#: src/xz/list.c:1140
msgid "--list works only on .xz files (--format=xz or --format=auto)"
msgstr "--list funktioniert nur mit .xz-Dateien (--format=xz oder --format=auto)"

#: src/xz/list.c:1146
msgid "--list does not support reading from standard input"
msgstr "--list unterstützt kein Lesen der Standardeingabe"

#: src/xz/main.c:89
#, c-format
msgid "%s: Error reading filenames: %s"
msgstr "%s: Fehler beim Lesen der Dateinamen: %s"

#: src/xz/main.c:96
#, c-format
msgid "%s: Unexpected end of input when reading filenames"
msgstr "%s: Unerwartetes Ende beim Lesen der Dateinamen"

#: src/xz/main.c:120
#, c-format
msgid "%s: Null character found when reading filenames; maybe you meant to use `--files0' instead of `--files'?"
msgstr "%s: Null-Zeichen gefunden beim Lesen der Dateinamen; Meinten Sie `--files0' statt `--files'?"

#: src/xz/main.c:174
msgid "Compression and decompression with --robot are not supported yet."
msgstr "Komprimierung und Dekomprimierung mit --robot ist noch nicht unterstützt."

#: src/xz/main.c:249
msgid "Cannot read data from standard input when reading filenames from standard input"
msgstr "Lesen der Standardeingabe ist nicht möglich, wenn die Dateinamen auch von der Standardeingabe gelesen werden"

#. TRANSLATORS: This is the program name in the beginning
#. of the line in messages. Usually it becomes "xz: ".
#. This is a translatable string because French needs
#. a space before a colon.
#: src/xz/message.c:714
#, c-format
msgid "%s: "
msgstr "%s: "

#: src/xz/message.c:777 src/xz/message.c:827
msgid "Internal error (bug)"
msgstr "Interner Fehler (Bug)"

#: src/xz/message.c:784
msgid "Cannot establish signal handlers"
msgstr "Kann Signalroutine nicht setzen"

#: src/xz/message.c:793
msgid "No integrity check; not verifying file integrity"
msgstr "Kein Integritäts-Check; werde Datei-Integrität nicht überprüfen"

#: src/xz/message.c:796
msgid "Unsupported type of integrity check; not verifying file integrity"
msgstr "Typ des Integritäts-Checks nicht unterstützt; werde Datei-Integrität nicht überprüfen"

#: src/xz/message.c:803
msgid "Memory usage limit reached"
msgstr "Speichernutzungslimit erreicht"

#: src/xz/message.c:806
msgid "File format not recognized"
msgstr "Dateiformat nicht erkannt"

#: src/xz/message.c:809
msgid "Unsupported options"
msgstr "Optionen nicht unterstützt"

#: src/xz/message.c:812
msgid "Compressed data is corrupt"
msgstr "Komprimierte Daten sind korrupt"

#: src/xz/message.c:815
msgid "Unexpected end of input"
msgstr "Unerwartetes Ende der Eingabe"

#: src/xz/message.c:848
#, c-format
msgid "%s MiB of memory is required. The limiter is disabled."
msgstr "%s MiB Speicher wird benötigt. Der Begrenzer ist deaktiviert."

#: src/xz/message.c:876
#, c-format
msgid "%s MiB of memory is required. The limit is %s."
msgstr "%s MiB Speicher wird benötigt. Limit ist %s."

#: src/xz/message.c:1043
#, c-format
msgid "%s: Filter chain: %s\n"
msgstr "%s: Filterkette: %s\n"

#: src/xz/message.c:1053
#, c-format
msgid "Try `%s --help' for more information."
msgstr "Versuchen Sie `%s --help' für mehr Informationen."

#: src/xz/message.c:1079
#, c-format
msgid ""
"Usage: %s [OPTION]... [FILE]...\n"
"Compress or decompress FILEs in the .xz format.\n"
"\n"
msgstr ""
"Benutzung: %s [OPTION]... [DATEI]...\n"
"Komprimiert oder dekomprimiert .xz-DATEI(EN).\n"
"\n"

#: src/xz/message.c:1086
msgid "Mandatory arguments to long options are mandatory for short options too.\n"
msgstr ""
"Obligatorische Argumente für lange Optionen sind auch für kurze Optionen\n"
"zwingend.\n"

#: src/xz/message.c:1090
msgid " Operation mode:\n"
msgstr " Operationsmodus:\n"

#: src/xz/message.c:1093
msgid ""
"  -z, --compress      force compression\n"
"  -d, --decompress    force decompression\n"
"  -t, --test          test compressed file integrity\n"
"  -l, --list          list information about .xz files"
msgstr ""
"  -z, --compress        Erzwinge Komprimierung\n"
"  -d, --decompress      Erzwinge Dekomprimierung\n"
"  -t, --test            Überprüfe Dateiintegrität\n"
"  -l, --list            Führe Dateiinformationen auf"

#: src/xz/message.c:1099
msgid ""
"\n"
" Operation modifiers:\n"
msgstr ""
"\n"
" Operationsmodifikatoren:\n/*
 *  OpenVPN -- An application to securely tunnel IP networks
 *             over a single TCP/UDP port, with support for SSL/TLS-based
 *             session authentication and key exchange,
 *             packet encryption, packet authentication, and
 *             packet compression.
 *
 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program (see the file COPYING included with this
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * Support routines for adding/deleting network routes.
 */

#include "syshead.h"

#include "common.h"
#include "error.h"
#include "route.h"
#include "misc.h"
#include "socket.h"
#include "manage.h"
#include "win32.h"

#include "memdbg.h"

static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);

#ifdef ENABLE_DEBUG

static void
print_bypass_addresses (const struct route_bypass *rb)
{
  struct gc_arena gc = gc_new ();
  int i;
  for (i = 0; i < rb->n_bypass; ++i)
    {
      msg (D_ROUTE, "ROUTE: bypass_host_route[%d]=%s",
	   i,
	   print_in_addr_t (rb->bypass[i], 0, &gc));
    }
  gc_free (&gc);
}

#endif

static bool
add_bypass_address (struct route_bypass *rb, const in_addr_t a)
{
  int i;
  for (i = 0; i < rb->n_bypass; ++i)
    {
      if (a == rb->bypass[i]) /* avoid duplicates */
	return true;
    }
  if (rb->n_bypass < N_ROUTE_BYPASS)
    {
      rb->bypass[rb->n_bypass++] = a;
      return true;
    }
  else
    {
      return false;
    }
}

struct route_option_list *
new_route_option_list (const int max_routes, struct gc_arena *a)
{
  struct route_option_list *ret;
  ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_option_list, struct route_option, max_routes, a);
  ret->capacity = max_routes;
  return ret;
}

struct route_option_list *
clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
{
  msgstr ""
"  -e, --extreme         Versuche durch stärkere CPU-Nutzung das Kompressions-\n"
"                        verhältnis zu verbessern. Dies beeinflusst nicht den\n"
"                        Speicherbedarf des Dekomprimierers."

#: src/xz/message.c:1139
msgid ""
"  -T, --threads=NUM   use at most NUM threads; the default is 1; set to 0\n"
"                      to use as many threads as there are processor cores"
msgstr ""
"  -T, --threads=ZAHL    Erzeuge höchstens ZAHL viele Threads; die Grund-\n"
"                        einstellung ist 1. Wenn der Wert 0 angegeben wird, dann\n"
"                        werden so viele Threads erzeugt, wie es Prozessorkerne\n"
"                        gibt"

#: src/xz/message.c:1144
msgid ""
"      --block-size=SIZE\n"
"                      start a new .xz block after every SIZE bytes of input;\n"
"                      use this to set the block size for threaded compression"
msgstr ""
"      --block-size=GRÖẞE\n"
"                        Beginne einen neuen .xz-Block nach GRÖẞE Bytes Eingabe;\n"
"                        Benutzen Sie diese Option um die Block Größe für\n"
"                        Komprimierung mit mehreren Threads zu setzen"

#: src/xz/message.c:1148
msgid ""
"      --block-list=SIZES\n"
"                      start a new .xz block after the given comma-separated\n"
"                      intervals of uncompressed data"
msgstr ""
"      --block-list=GRÖẞEN\n"
"                        Beginne einen neuen .xz-Block gemäß der angegebenen,\n"
"                        durch Kommata getrennten Intervalle an unkomprimierten\n"
"                        Daten"

#: src/xz/message.c:1152
msgid ""
"      --flush-timeout=TIMEOUT\n"
"                      when compressing, if more than TIMEOUT milliseconds has\n"
"                      passed since the previous flush and reading more input\n"
"                      would block, all pending data is flushed out"
msgstr ""
"      --flush-timeout=ZEITÜBERSCHREITUNG\n"
"                        Wenn beim Komprimieren mehr als ZEITÜBERSCHREITUNG\n"
"                        Millisekunden seit der letzten Flush-Operation ver-\n"
"                        gangen sind und das Lesen von zusätzlichen Eingabe-\n"
"                        daten den Prozess blockieren würde, dann werden alle\n"
"                        noch ausstehenden Daten geschrieben"

#: src/xz/message.c:1158
#, no-c-format
msgid ""
"      --memlimit-compress=LIMIT\n"
"      --memlimit-decompress=LIMIT\n"
"  -M, --memlimit=LIMIT\n"
"                      set memory usage limit for compression, decompression,\n"
"                      or both; LIMIT is in bytes, % of RAM, or 0 for defaults"
msgstr ""
"      --memlimit-compress=LIMIT\n"
"      --memlimit-decompress=LIMIT\n"
"  -M, --memlimit=LIMIT  Setze Speichernutzungslimit für Komprimierung,\n"
"                        Dekomprimierung, oder beides; LIMIT ist in Bytes, % RAM,\n"
"                        oder 0 für Verwenden der Grundeinstellungen."

#: src/xz/message.c:1165
msgid ""
"      --no-adjust     if compression settings exceed the memory usage limit,\n"
"                      give an error instead of adjusting the settings downwards"
msgstr ""
"      --no-adjust       Wenn die Kompressionseinstellungen das Speicher-\n"
"                        nutzungslimit übersteigen, erzeuge einen Fehler statt\n"
"                        die Einstellungen nach unten anzupassen."

#: src/xz/message.c:1171
msgid ""
"\n"
" Custom filter chain for compression (alternative for using presets):"
msgstr ""
"\n"
" Benutzerdef. Filterkette für Komprimierung (alternativ zu Voreinstellung):"

#: src/xz/message.c:1180
msgid ""
"\n"
"  --lzma1[=OPTS]      LZMA1 or LZMA2; OPTS is a comma-separated list of zero or\n"
"  --lzma2[=OPTS]      more of the following options (valid values; default):\n"
"                        preset=PRE reset options to a preset (0-9[e])\n"
"                        dict=NUM   dictionary size (4KiB - 1536MiB; 8MiB)\n"
"                        lc=NUM     number of literal context bits (0-4; 3)\n"
"                        lp=NUM     number of literal position bits (0-4; 0)\n"
"                        pb=NUM     number of position bits (0-4; 2)\n"
"                        mode=MODE  compression mode (fast, normal; normal)\n"
"                        nice=NUM   nice length of a match (2-273; 64)\n"
"                        mf=NAME    match finder (hc3, hc4, bt2, bt3, bt4; bt4)\n"
"                        depth=NUM  maximum search depth; 0=automatic (default)"
msgstr ""
"\n"
"  --lzma1[=OPTIONEN]    LZMA1 oder LZMA2; OPTIONEN ist eine durch Kommata\n"
"  --lzma2[=OPTIONEN]    Getrennte Liste bestehend aus den folgenden Optionen\n"
"                        (zulässige Werte; Voreinstellung):\n"
"                          preset=NUM Setze Optionen zurück zu Voreinstellung\n"
"                                     (0-9[e])\n"
"                          dict=NUM   Wörterbuchgröße (4 KiB - 1536 MiB; 8 MiB)\n"
"                          lc=NUM     Anzahl der Literal-Kontext-Bits (0-4; 3)\n"
"                          lp=NUM     Anzahl der Literal-Positions-Bits (0-4; 0)\n"
"                          pb=NUM     Anzahl der Positions-Bits (0-4; 2)\n"
"                          mode=MODUS Kompressionsmodus (fast, normal; normal)\n"
"                          nice=NUM   Nice-Länge eines Treffers (2-273; 64)\n"
"                          mf=NAME    Algorithmus zum Auffinden von\n"
"                                     Übereinstimmungen (hc3, hc4, bt2, bt3, bt4;\n"
"                                     bt4)\n"
"                          depth=NUM  Maximale Suchtiefe; 0=automatisch\n"
"                                     (Voreinstellung)"

#: src/xz/message.c:1195
msgid ""
"\n"
"  --x86[=OPTS]        x86 BCJ filter (32-bit and 64-bit)\n"
"  --powerpc[=OPTS]    PowerPC BCJ filter (big endian only)\n"
"  --ia64[=OPTS]       IA-64 (Itanium) BCJ filter\n"
"  --arm[=OPTS]        ARM BCJ filter (little endian only)\n"
"  --armthumb[=OPTS]   ARM-Thumb BCJ filter (little endian only)\n"
"  --sparc[=OPTS]      SPARC BCJ filter\n"
"                      Valid OPTS for all BCJ filters:\n"
"                        start=NUM  start offset for conversions (default=0)"
msgstr ""
"\n"
"  --x86[=OPTIONEN]      x86 BCJ-Filter (32-bit und 64-bit)\n"
"  --powerpc[=OPTIONEN]  PowerPC-BCJ-Filter (nur Big Endian)\n"
"  --ia64[=OPTIONEN]     IA64-(Itanium-)BCJ-Filter\n"
"  --arm[=OPTIONEN]      ARM-BCJ-Filter (nur Little Endian)\n"
"  --armthumb[=OPTIONEN] ARM-Thumb-BCJ-Filter (nur Little Endian)\n"
"  --sparc[=OPTIONEN]    SPARC-BCJ-Filter\n"
"                        Zulässige Optionen für alle BCJ-Filter:\n"
"                          start=NUM  Start-Offset für Konversion\n"
"                          (Voreinstellung=0)"

#: src/xz/message.c:1207
msgid ""
"\n"
"  --delta[=OPTS]      Delta filter; valid OPTS (valid values; default):\n"
"                        dist=NUM   distance between bytes being subtracted\n"
"                                   from each other (1-256; 1)"
msgstr ""
"\n"
"  --delta[=OPTIONEN]    Delta-Filter; zulässige Optionen (gültige Werte;\n"
"                        Voreinstellung):\n"
"                          dist=NUM  Abstand zwischen den Bytes, die voneinander\n"
"                                    subtrahiert werden (1-256; 1)"

#: src/xz/message.c:1215
msgid ""
"\n"
" Other options:\n"
msgstr ""
"\n"
" Andere Optionen:\n"

#: src/xz/message.c:1218
msgid ""
"  -q, --quiet         suppress warnings; specify twice to suppress errors too\n"
"  -v, --verbose       be verbose; specify twice for even more verbose"
msgstr ""
"  -q, --quiet           Unterdrücke Warnungen; benutze diese Option zweimal\n"
"                        um auch Fehlermeldungen zu unterdrücken\n"
"  -v, --verbose         Sei gesprächig; benutze diese Option zweimal um noch\n"
"                        gesprächiger zu sein"

#: src/xz/message.c:1223
msgid "  -Q, --no-warn       make warnings not affect the exit status"
msgstr "  -Q, --no-warn         Warnungen verändern nicht den Exit Status"

#: src/xz/message.c:1225
msgid "      --robot         use machine-parsable messages (useful for scripts)"
msgstr ""
"      --robot           Benutze maschinen-lesbare Meldungen (nützlich für\n"
"                        Skripte)"

#: src/xz/message.c:1228
msgid ""
"      --info-memory   display the total amount of RAM and the currently active\n"
"                      memory usage limits, and exit"
msgstr "      --info-memory     Zeige Speicherlimit an und terminiere"

#: src/xz/message.c:1231
msgid ""
"  -h, --help          display the short help (lists only the basic options)\n"
"  -H, --long-help     display this long help and exit"
msgstr ""
"  -h, --help            Zeige kurze Hilfe an (zeigt nur die grundlegenden\n"
"                        Optionen)\n"
"  -H, --long-help       Zeige diese lange Hilfe an und terminiere"

#: src/xz/message.c:1235
->metric = atoi (ro->metric);
      if (r->metric < 0)
	{
	  msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
	       ro->network,
	       ro->metric);
	  goto fail;
	}
      r->metric_defined = true;
    }
  else if (spec->default_metric_defined)
    {
      r->metric = spec->default_metric;
      r->metric_defined = true;
    }

  r->defined = true;

  return true;

 fail:
  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
       ro->network);
  r->defined = false;
  return false;
}

void
add_route_to_option_list (struct route_option_list *l,
			  const char *network,
			  const char *netmask,
			  const char *gateway,
			  const char *metric)
{
  struct route_option *ro;
  if (l->n >= l->capacity)
    msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d routes -- please increase the max-routes option in the client configuration file",
	 l->capacity);
  ro = &l->routes[l->n];
  ro->network = network;
  ro->netmask = netmask;
  ro->gateway = gateway;
  ro->metric = metric;
  ++l->n;
}

void
clear_route_list (struct route_list *rl)
{
  const int capacity = rl->capacity;
  const size_t rl_size = array_mult_safe (sizeof(struct route), capacity, sizeof(struct route_list));
  memset(rl, 0, rl_size);
  rl->capacity = capacity;
}

void
route_list_add_default_gateway (struct route_list *rl,
				struct env_set *es,
				const in_addr_t addr)
{
  rl->spec.remote_endpoint = addr;
  rl->spec.remote_endpoint_defined = true;
  setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
}

bool
init_route_list (struct route_list *rl,
		 const struct route_option_list *opt,
		 const char *remote_endpoint,
		 int default_metric,
		 in_addr_t remote_host,
		 struct env_set *es)
{
  struct gc_arena gc = gc_new ();
  bool ret = true;

  clear_route_list (rl);

  rl->flags = opt->flags;

  if (remote_host)
    {
      rl->spec.remote_host = remote_host;
      rl->spec.remote_host_defined = true;
    }

  if (default_metric)
    {
      rl->spec.default_metric = default_metric;
      rl->spec.default_metric_defined = true;
    }

  rl->spec.net_gateway_defined = get_default_gateway (&rl->spec.net_gateway, NULL);
  if (rl->spec.net_gateway_defined)
    {
      setenv_route_addr (es, "net_gateway", rl->spec.net_gateway, -1);
      dmsg (D_ROUTE, "ROUTE default_gateway=%s", print_in_addr_t (rl->spec.net_gateway, 0, &gc));
    }
  else
    {
      dmsg (D_ROUTE, "ROUTE: default_gateway=UNDEF");
    }

  if (rl->flags & RG_ENABLE)
    {
      get_bypass_addresses (&rl->spec.bypass, rl->flags);
#ifdef ENABLE_DEBUG
      print_bypass_addresses (&rl->spec.bypass);
#endif
    }

  if (is_route_parm_defined (remote_endpoint))
    {
      rl->spec.remote_endpoint = getaddr (
				     GETADDR_RESOLVE
				     | GETADDR_HOST_ORDER
				     | GETADDR_WARN_ON_SIGNAL,
				     remote_endpoint,
				     0,
				     &rl->spec.remote_endpoint_defined,
				     NULL);

      if (rl->spec.remote_endpoint_defined)
	{
	  setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
	}
      else
	{
	  msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s",
	       remote_endpoint);
	  ret = false;
	}
    }
  else
    rl->spec.remote_endpoint_defined = false;

  /* parse the routes from opt to rl */
  {
    int i, j = 0;
    bool warned = false;
    for (i = 0; i < opt->n; ++i)
      {
	struct resolve_list netlist;
	struct route r;
	int k;

	if (!init_route (&r,
			 &netlist,
			 &opt->routes[i],
			 &rl->spec))
	  ret = false;
	else
	  {
	    if (!netlist.len)
	      {
		netlist.data[0] = r.network;
		netlist.len = 1;
	      }
	    for (k = 0; k < netlist.len; ++k)
	      {
		if (j < rl->capacity)
		  {
		    r.network = netlist.data[k];
		    rl->routes[j++] = r;
		  }
		else
		  {
		    if (!warned)
		      {
			msg (M_WARN, PACKAGE_NAME " ROUTE: routes dropped because number of expanded routes is greater than route list capacity (%d)", rl->capacity);
			warned = true;
		      }
		  }
	      }
	  }
      }
    rl->n = j;
  }

  gc_free (&gc);
  return ret;
}

static void
add_route3 (in_addr_t network,
	    in_addr_t netmask,
	    in_addr_t gateway,
	    const struct tuntap *tt,
	    unsigned int flags,
	    const struct env_set *es)
{
  struct route r;
  CLEAR (r);
  r.defined = true;
  r.network = network;
  r.netmask = netmask;
  r.gateway = gateway;
  add_route (&r, tt, flags, es);
}

static void
del_route3 (in_addr_t network,
	    in_addr_t netmask,
	    in_addr_t gateway,
	    const struct tuntap *tt,
	    unsigned int flags,
	    const struct env_set *es)
{
  struct route r;
  CLEAR (r);
  r.defined = true;
  r.network = network;
  r.netmask = netmask;
  r.gateway = gateway;
  delete_route (&r, tt, flags, es);
}

static void
add_bypass_routes (struct route_bypass *rb,
		   in_addr_t gateway,
		   const struct tuntap *tt,
		   unsigned int flags,
		   const struct env_set *es)
{
  int i;
  for (i = 0; i < rb->n_bypass; ++i)
    {
      if (rb->bypass[i] != gateway)
	add_route3 (rb->bypass[i],
		    ~0,
		    gateway,
		    tt,
		    flags,
		    es);
    }
}

static void
del_bypass_routes (struct route_bypass *rb,
		   in_addr_t gateway,
		   const struct tuntap *tt,
		   unsigned int flags,
		   const struct env_set *es)
{
  int i;
  for (i = 0; i < rb->n_bypass; ++i)
    {
      if (rb->bypass[i] != gateway)
	del_route3 (rb->bypass[i],
		    ~0,
		    gateway,
		    tt,
		    flags,
		    es);
    }
}

static void
redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  const char err[] = "NOTE: unable to redirect default gateway --";

  if (rl->flags & RG_ENABLE)
    {
      if (!rl->spec.remote_endpoint_defined)
	{
	  msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err);
	}
      else if (!rl->spec.net_gateway_defined)
	{
	  msg (M_WARN, "%s Cannot read current default gateway from system", err);
	}
      else if (!rl->spec.remote_host_defined)
	{
	  msg (M_WARN, "%s Cannot obtain current remote host address", err);
	}
      else
	{
	  bool local = BOOL_CAST(rl->flags & RG_LOCAL);
	  if (rl->flags & RG_AUTO_LOCAL) {
	    const int tla = test_local_addr (rl->spec.remote_host);
	    if (tla == TLA_NONLOCAL)
	      {
		dmsg (D_ROUTE, "ROUTE remote_host is NOT LOCAL");
		local = false;
	      }
	    else if (tla == TLA_LOCAL)
	      {
		dmsg (D_ROUTE, "ROUTE remote_host is LOCAL");
		local = true;
	      }
	  }
	  if (!local)
	    {
	      /* route remote host to original default gateway */
	      add_route3 (rl->spec.remote_host,
			  ~0,
			  rl->spec.net_gateway,
			  tt,
			  flags,
			  es);
	      rl->did_local = true;
	    }

	  /* route DHCP/DNS server traffic through original default gateway */
	  add_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);

	  if (rl->flags & RG_REROUTE_GW)
	    {
	      if (rl->flags & RG_DEF1)
		{
		  /* add new default route (1st component) */
		  add_route3 (0x00000000,
			      0x80000000,
			      rl->spec.remote_endpoint,
			      tt,
			      flags,
			      es);

		  /* add new default route (2nd component) */
		  add_route3 (0x80000000,
			      0x80000000,
			      rl->spec.remote_endpoint,
			      tt,
			      flags,
			      es);
		}
	      else
		{
		  /* delete default route */
		  del_route3 (0,
			      0,
			      rl->spec.net_gateway,
			      tt,
			      flags,
			      es);

		  /* add new default route */
		  add_route3 (0,
			      0,
			      rl->spec.remote_endpoint,
			      tt,
			      flags,
			      es);
		}
	    }

	  /* set a flag so we can undo later */
	  rl->did_redirect_default_gateway = true;
	}
    }
}

static void
undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  if (rl->did_redirect_default_gateway)
    {
      /* delete remote host route */
      if (rl->did_local)
	{
	  del_route3 (rl->spec.remote_host,
		      ~0,
		      rl->spec.net_gateway,
		      tt,
		      flags,
		      es);
	  rl->did_local = false;
	}

      /* delete special DHCP/DNS bypass route */
      del_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);

      if (rl->flags & RG_REROUTE_GW)
	{
	  if (rl->flags & RG_DEF1)
	    {
	      /* delete default route (1st component) */
	      del_route3 (0x00000000,
			  0x80000000,
			  rl->spec.remote_endpoint,
			  tt,
			  flags,
			  es);

	      /* delete default route (2nd component) */
	      del_route3 (0x80000000,
			  0x80000000,
			  rl->spec.remote_endpoint,
			  tt,
			  flags,
			  es);
	    }
	  else
	    {
	      /* delete default route */
	      del_route3 (0,
			  0,
			  rl->spec.remote_endpoint,
			  tt,
			  flags,
			  es);

	      /* restore original default route */
	      add_route3 (0,
			  0,
			  rl->spec.net_gateway,
			  tt,
			  flags,
			  es);
	    }
	}

      rl->did_redirect_default_gateway = false;
    }
}

void
add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  redirect_default_route_to_vpn (rl, tt, flags, es);
  if (!rl->routes_added)
    {
      int i;

#ifdef ENABLE_MANAGEMENT
      if (management && rl->n)
	{
	  management_set_state (management,
				OPENVPN_STATE_ADD_ROUTES,
				NULL,
				0,
				0);
	}
#endif
      
      for (i = 0; i < rl->n; ++i)
	{
	  struct route *r = &rl->routes[i];
	  check_subnet_conflict (r->network, r->netmask, "route");
	  if (flags & ROUTE_DELETE_FIRST)
	    delete_route (r, tt, flags, es);
	  add_route (r, tt, flags, es);
	}
      rl->routes_added = true;
    }
}

void
delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  if (rl->routes_added)
    {
      int i;
      for (i = rl->n - 1; i >= 0; --i)
	{
	  const struct route *r = &rl->routes[i];
	  delete_route (r, tt, flags, es);
	}
      rl->routes_added = false;
    }
  undo_redirect_default_route_to_vpn (rl, tt, flags, es);

  clear_route_list (rl);
}

#ifdef ENABLE_DEBUG

static const char *
show_opt (const char *option)
{
  if (!option)
    return "nil";
  else
    return option;
}

static void
print_route_option (const struct route_option *ro, int level)
{
  msg (level, "  route %s/%s/%s/%s",
       show_opt (ro->network),
       show_opt (ro->netmask),
       show_opt (ro->gateway),
       show_opt (ro->metric));
}

void
print_route_options (const struct route_option_list *rol,
		     int level)
{
  int i;
  if (rol->flags & RG_ENABLE)
    msg (level, "  [redirect_default_gateway local=%d]",
	 (rol->flags & RG_LOCAL) != 0);
  for (i = 0; i < rol->n; ++i)
    print_route_option (&rol->routes[i], level);
}

#endif

static void
print_route (const struct route *r, int level)
{
  struct gc_arena gc = gc_new ();
  if (r->defined)
    msg (level, "%s", route_string (r, &gc));
  gc_free (&gc);
}

void
print_routes (const struct route_list *rl, int level)
{
  int i;
  for (i = 0; i < rl->n; ++i)
    print_route (&rl->routes[i], level);
}

static void
setenv_route (struct env_set *es, const struct route *r, int i)
{
  struct gc_arena gc = gc_new ();
  if (r->defined)
    {
      setenv_route_addr (es, "network", r->network, i);
      setenv_route_addr (es, "netmask", r->netmask, i);
      setenv_route_addr (es, "gateway", r->gateway, i);

      if (r->metric_defined)
	{
	  struct buffer name = alloc_buf_gc (256, &gc);
	  buf_printf (&name, "route_metric_%d", i);
	  setenv_int (es, BSTR (&name), r->metric);
	}
    }
  gc_free (&gc);
}

void
setenv_routes (struct env_set *es, const struct route_list *rl)
{
  int i;
  for (i = 0; i < rl->n; ++i)
    setenv_route (es, &rl->routes[i], i + 1);
}

void
add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  struct gc_arena gc;
  struct argv argv;
  const char *network;
  const char *netmask;
  const char *gateway;
  bool status = false;

  if (!r->defined)
    return;

  gc_init (&gc);
  argv_init (&argv);

  network = print_in_addr_t (r->network, 0, &gc);
  netmask = print_in_addr_t (r->netmask, 0, &gc);
  gateway = print_in_addr_t (r->gateway, 0, &gc);

  /*
   * Filter out routes which are essentially no-ops
   */
  if (r->network == r->gateway && r->netmask == 0xFFFFFFFF)
    {
      msg (M_INFO, PACKAGE_NAME " ROUTE: omitted no-op route: %s/%s -> %s",
	   network, netmask, gateway);
      goto done;
    }

#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
  argv_printf (&argv, "%s route add %s/%d via %s",
  	      iproute_path,
	      network,
	      count_netmask_bits(netmask),
	      gateway);
  if (r->metric_defined)
    argv_printf_cat (&argv, "metric %d", r->metric);

#else
  argv_printf (&argv, "%s add -net %s netmask %s gw %s",
		ROUTE_PATH,
	      network,
	      netmask,
	      gateway);
  if (r->metric_defined)
    argv_printf_cat (&argv, "metric %d", r->metric);
#endif  /*CONFIG_FEATURE_IPROUTE*/
  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");

#elif defined (WIN32)

  argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
	       get_win_sys_path(),
	       WIN_ROUTE_PATH_SUFFIX,
	       network,
	       netmask,
	       gateway);
  if (r->metric_defined)
    argv_printf_cat (&argv, "METRIC %d", r->metric);

  argv_msg (D_ROUTE, &argv);

  if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
    {
      status = add_route_ipapi (r, tt);
      msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
    }
  else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
    {
      netcmd_semaphore_lock ();
      status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
      netcmd_semaphore_release ();
    }
  else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
    {
      status = add_route_ipapi (r, tt);
      msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
      if (!status)
	{
	  msg (D_ROUTE, "Route addition fallback to route.exe");
	  netcmd_semaphore_lock ();
	  status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
	  netcmd_semaphore_release ();
	}
    }
  else
    {
      ASSERT (0);
    }

#elif defined (TARGET_SOLARIS)

  /* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */

  argv_printf (&argv, "%s add",
		ROUTE_PATH);

#if 0
  if (r->metric_defined)
    argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif

  argv_printf_cat (&argv, "%s -netmask %s %s",
	      network,
	      netmask,
	      gateway);

  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");

#elif defined(TARGET_FREEBSD)

  argv_printf (&argv, "%s add",
		ROUTE_PATH);

#if 0
  if (r->metric_defined)
    argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif

  argv_printf_cat (&argv, "-net %s %s %s",
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");

#elif defined(TARGET_DRAGONFLY)

  argv_printf (&argv, "%s add",
		ROUTE_PATH);

#if 0
  if (r->metric_defined)
    argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif

  argv_printf_cat (&argv, "-net %s %s %s",
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");

#elif defined(TARGET_DARWIN)

  argv_printf (&argv, "%s add",
		ROUTE_PATH);

#if 0
  if (r->metric_defined)
    argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif

  argv_printf_cat (&argv, "-net %s %s %s",
              network,
              gateway,
              netmask);

  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");

#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)

  argv_printf (&argv, "%s add",
		ROUTE_PATH);

#if 0
  if (r->metric_defined)
    argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif

  argv_printf_cat (&argv, "-net %s %s -netmask %s",
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");

#else
  msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system.  Try putting your routes in a --route-up script");
#endif

 done:
  r->defined = status;
  argv_reset (&argv);
  gc_free (&gc);
}

static void
delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
  struct gc_arena gc;
  struct argv argv;
  const char *network;
  const char *netmask;
  const char *gateway;

  if (!r->defined)
    return;

  gc_init (&gc);
  argv_init (&argv);

  network = print_in_addr_t (r->network, 0, &gc);
  netmask = print_in_addr_t (r->netmask, 0, &gc);
  gateway = print_in_addr_t (r->gateway, 0, &gc);

#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
  argv_printf (&argv, "%s route del %s/%d",
  	      iproute_path,
	      network,
	      count_netmask_bits(netmask));
#else

  argv_printf (&argv, "%s del -net %s netmask %s",
		ROUTE_PATH,
	      network,
	      netmask);
#endif /*CONFIG_FEATURE_IPROUTE*/
  if (r->metric_defined)
    argv_printf_cat (&argv, "metric %d", r->metric);
  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");

#elif defined (WIN32)
  
  argv_printf (&argv, "%s%sc DELETE %s MASK %s %s",
	       get_win_sys_path(),
	       WIN_ROUTE_PATH_SUFFIX,
	       network,
	       netmask,
	       gateway);

  argv_msg (D_ROUTE, &argv);

  if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
    {
      const bool status = del_route_ipapi (r, tt);
      msg (D_ROUTE, "Route deletion via IPAPI %s", status ? "succeeded" : "failed");
    }
  else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
    {
      netcmd_semaphore_lock ();
      openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
      netcmd_semaphore_release ();
    }
  else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
    {
      const bool status = del_route_ipapi (r, tt);
      msg (D_ROUTE, "Route deletion via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
      if (!status)
	{
	  msg (D_ROUTE, "Route deletion fallback to route.exe");
	  netcmd_semaphore_lock ();
	  openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
	  netcmd_semaphore_release ();
	}
    }
  else
    {
      ASSERT (0);
    }

#elif defined (TARGET_SOLARIS)

  argv_printf (&argv, "%s delete %s -netmask %s %s",
		ROUTE_PATH,
	      network,
	      netmask,
	      gateway);

  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");

#elif defined(TARGET_FREEBSD)

  argv_printf (&argv, "%s delete -net %s %s %s",
		ROUTE_PATH,
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");

#elif defined(TARGET_DRAGONFLY)

  argv_printf (&argv, "%s delete -net %s %s %s",
		ROUTE_PATH,
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");

#elif defined(TARGET_DARWIN)

  argv_printf (&argv, "%s delete -net %s %s %s",
		ROUTE_PATH,
              network,
              gateway,
              netmask);

  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");

#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)

  argv_printf (&argv, "%s delete -net %s %s -netmask %s",
		ROUTE_PATH,
	      network,
	      gateway,
	      netmask);

  argv_msg (D_ROUTE, &argv);
  openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");

#else
  msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system.  Try putting your routes in a --route-up script");
#endif

  argv_reset (&argv);
  gc_free (&gc);
}

/*
 * The --redirect-gateway option requires OS-specific code below
 * to get the current default gateway.
 */

#if defined(WIN32)

static const MIB_IPFORWARDTABLE *
get_windows_routing_table (struct gc_arena *gc)
{
  ULONG size = 0;
  PMIB_IPFORWARDTABLE rt = NULL;
  DWORD status;

  status = GetIpForwardTable (NULL, &size, TRUE);
  if (status == ERROR_INSUFFICIENT_BUFFER)
    {
      rt = (PMIB_IPFORWARDTABLE) gc_malloc (size, false, gc);
      status = GetIpForwardTable (rt, &size, TRUE);
      if (status != NO_ERROR)
	{
	  msg (D_ROUTE, "NOTE: GetIpForwardTable returned error: %s (code=%u)",
	       strerror_win32 (status, gc),
	       (unsigned int)status);
	  rt = NULL;
	}
    }
  return rt;
}

static int
test_route (const IP_ADAPTER_INFO *adapters,
	    const in_addr_t gateway,
	    DWORD *index)
{
  int count = 0;
  DWORD i = adapter_index_of_ip (adapters, gateway, &count, NULL);
  if (index)
    *index = i;
  return count;
}

static void
test_route_helper (bool *ret,
		   int *count,
		   int *good,
		   int *ambig,
		   const IP_ADAPTER_INFO *adapters,
		   const in_addr_t gateway)
{
  int c;

  ++*count;
  c = test_route (adapters, gateway, NULL);
  if (c == 0)
    *ret = false;
  else
    ++*good;
  if (c > 1)
    ++*ambig;
}

/*
 * If we tried to add routes now, would we succeed?
 */
bool
test_routes (const struct route_list *rl, const struct tuntap *tt)
{
  struct gc_arena gc = gc_new ();
  const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
  bool ret = false;
  int count = 0;
  int good = 0;
  int ambig = 0;
  bool adapter_up = false;

  if (is_adapter_up (tt, adapters))
    {
      ret = true;
      adapter_up = true;

      if (rl)
	{
	  int i;
	  for (i = 0; i < rl->n; ++i)
	    test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway);

	  if ((rl->flags & RG_ENABLE) && rl->spec.remote_endpoint_defined)
	    test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint);
	}
    }

  msg (D_ROUTE, "TEST ROUTES: %d/%d succeeded len=%d ret=%d a=%d u/d=%s",
       good,
       count,
       rl ? rl->n : -1,
       (int)ret,
       ambig,
       adapter_up ? "up" : "down");

  gc_free (&gc);
  return ret;
}

static const MIB_IPFORWARDROW *
get_default_gateway_row (const MIB_IPFORWARDTABLE *routes)
{
  struct gc_arena gc = gc_new ();
  DWORD lowest_metric = ~0;
  const MIB_IPFORWARDROW *ret = NULL;
  int i;
  int best = -1;

  if (routes)
    {
      for (i = 0; i < routes->dwNumEntries; ++i)
	{
	  const MIB_IPFORWARDROW *row = &routes->table[i];
	  const in_addr_t net = ntohl (row->dwForwardDest);
	  const in_addr_t mask = ntohl (row->dwForwardMask);
	  const DWORD index = row->dwForwardIfIndex;
	  const DWORD metric = row->dwForwardMetric1;

	  dmsg (D_ROUTE_DEBUG, "GDGR: route[%d] %s/%s i=%d m=%d",
		i,
		print_in_addr_t ((in_addr_t) net, 0, &gc),
		print_in_addr_t ((in_addr_t) mask, 0, &gc),
		(int)index,
		(int)metric);

	  if (!net && !mask && metric < lowest_metric)
	    {
	      ret = row;
	      lowest_metric = metric;
	      best = i;
	    }
	}
    }

  dmsg (D_ROUTE_DEBUG, "GDGR: best=%d lm=%u", best, (unsigned int)lowest_metric);

  gc_free (&gc);
  return ret;
}

bool
get_default_gateway (in_addr_t *gw, in_addr_t *netmask)
{
  struct gc_arena gc = gc_new ();
  bool ret_bool = false;

  const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
  const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);
  const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);

  if (row)
    {
      *gw = ntohl (row->dwForwardNextHop);
      if (netmask)
	{
	  if (adapter_index_of_ip (adapters, *gw, NULL, netmask) == ~0)
	    *netmask = ~0;
	}
      ret_bool = true;
    }

  gc_free (&gc);
  return ret_bool;
}

static DWORD
windows_route_find_if_index (const struct route *r, const struct tuntap *tt)
{
  struct gc_arena gc = gc_new ();
  DWORD ret = ~0;
  int count = 0;
  const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
  const IP_ADAPTER_INFO *tun_adapter = get_tun_adapter (tt, adapters);
  bool on_tun = false;

  /* first test on tun interface */
  if (is_ip_in_adapter_subnet (tun_adapter, r->gateway, NULL))
    {
      ret = tun_adapter->Index;
      count = 1;
      on_tun = true;
    }
  else /* test on other interfaces */
    {
      count = test_route (adapters, r->gateway, &ret);
    }

  if (count == 0)
    {
      msg (M_WARN, "Warning: route gateway is not reachable on any active network adapters: %s",
	   print_in_addr_t (r->gateway, 0, &gc));
      ret = ~0;
    }
  else if (count > 1)
    {
      msg (M_WARN, "Warning: route gateway is ambiguous: %s (%d matches)",
	   print_in_addr_t (r->gateway, 0, &gc),
	   count);
      ret = ~0;
    }

  dmsg (D_ROUTE_DEBUG, "DEBUG: route find if: on_tun=%d count=%d index=%d",
       on_tun,
       count,
       (int)ret);

  gc_free (&gc);
  return ret;
}

bool
add_route_ipapi (const struct route *r, const struct tuntap *tt)
{
  struct gc_arena gc = gc_new ();
  bool ret = false;
  DWORD status;
  const DWORD if_index = windows_route_find_if_index (r, tt);  

  if (if_index != ~0)
    {
      MIB_IPFORWARDROW fr;
      CLEAR (fr);
      fr.dwForwardDest = htonl (r->network);
      fr.dwForwardMask = htonl (r->netmask);
      fr.dwForwardPolicy = 0;
      fr.dwForwardNextHop = htonl (r->gateway);
      fr.dwForwardIfIndex = if_index;
      fr.dwForwardType = 4;  /* the next hop is not the final dest */
      fr.dwForwardProto = 3; /* PROTO_IP_NETMGMT */
      fr.dwForwardAge = 0;
      fr.dwForwardNextHopAS = 0;
      fr.dwForwardMetric1 = r->metric_defined ? r->metric : 1;
      fr.dwForwardMetric2 = ~0;
      fr.dwForwardMetric3 = ~0;
      fr.dwForwardMetric4 = ~0;
      fr.dwForwardMetric5 = ~0;

      if ((r->network & r->netmask) != r->network)
	msg (M_WARN, "Warning: address %s is not a network address in relation to netmask %s",
	     print_in_addr_t (r->network, 0, &gc),
	     print_in_addr_t (r->netmask, 0, &gc));

      status = CreateIpForwardEntry (&fr);

      if (status == NO_ERROR)
	ret = true;
      else
	{
	  /* failed, try increasing the metric to work around Vista issue */
	  const unsigned int forward_metric_limit = 2048; /* iteratively retry higher metrics up to this limit */

	  for ( ; fr.dwForwardMetric1 <= forward_metric_limit; ++fr.dwForwardMetric1)
	    {
	      /* try a different forward type=3 ("the next hop is the final dest") in addition to 4.
		 --redirect-gateway over RRAS seems to need this. */
	      for (fr.dwForwardType = 4; fr.dwForwardType >= 3; --fr.dwForwardType)
		{
		  status = CreateIpForwardEntry (&fr);
		  if (status == NO_ERROR)
		    {
		      msg (D_ROUTE, "ROUTE: CreateIpForwardEntry succeeded with dwForwardMetric1=%u and dwForwardType=%u",
			   (unsigned int)fr.dwForwardMetric1,
			   (unsigned int)fr.dwForwardType);
		      ret = true;
		      goto doublebreak;
		    }
		  else if (status != ERROR_BAD_ARGUMENTS)
		    goto doublebreak;
		}
	    }

	doublebreak:
	  if (status != NO_ERROR)
	    msg (M_WARN, "ROUTE: route addition failed using CreateIpForwardEntry: %s [status=%u if_index=%u]",
		 strerror_win32 (status, &gc),
		 (unsigned int)status,
		 (unsigned int)if_index);
	}
    }

  gc_free (&gc);
  return ret;
}

bool
del_route_ipapi (const struct route *r, const struct tuntap *tt)
{
  struct gc_arena gc = gc_new ();
  bool ret = false;
  DWORD status;
  const DWORD if_index = windows_route_find_if_index (r, tt);

  if (if_index != ~0)
    {
      MIB_IPFORWARDROW fr;
      CLEAR (fr);

      fr.dwForwardDest = htonl (r->network);
      fr.dwForwardMask = htonl (r->netmask);
      fr.dwForwardPolicy = 0;
      fr.dwForwardNextHop = htonl (r->gateway);
      fr.dwForwardIfIndex = if_index;

      status = DeleteIpForwardEntry (&fr);

      if (status == NO_ERROR)
	ret = true;
      else
	msg (M_WARN, "ROUTE: route deletion failed using DeleteIpForwardEntry: %s",
	     strerror_win32 (status, &gc));
    }

  gc_free (&gc);
  return ret;
}

static const char *
format_route_entry (const MIB_IPFORWARDROW *r, struct gc_arena *gc)
{
  struct buffer out = alloc_buf_gc (256, gc);
  buf_printf (&out, "%s %s %s p=%d i=%d t=%d pr=%d a=%d h=%d m=%d/%d/%d/%d/%d", 
	      print_in_addr_t (r->dwForwardDest, IA_NET_ORDER, gc),
	      print_in_addr_t (r->dwForwardMask, IA_NET_ORDER, gc),
	      print_in_addr_t (r->dwForwardNextHop, IA_NET_ORDER, gc),
	      (int)r->dwForwardPolicy,
	      (int)r->dwForwardIfIndex,
	      (int)r->dwForwardType,
	      (int)r->dwForwardProto,
	      (int)r->dwForwardAge,
	      (int)r->dwForwardNextHopAS,
	      (int)r->dwForwardMetric1,
	      (int)r->dwForwardMetric2,
	      (int)r->dwForwardMetric3,
	      (int)r->dwForwardMetric4,
	      (int)r->dwForwardMetric5);
  return BSTR (&out);
}

/*
 * Show current routing table
 */
void
show_routes (int msglev)
{
  struct gc_arena gc = gc_new ();
  int i;

  const MIB_IPFORWARDTABLE *rt = get_windows_routing_table (&gc);

  msg (msglev, "SYSTEM ROUTING TABLE");
  if (rt)
    {
      for (i = 0; i < rt->dwNumEntries; ++i)
	{
	  msg (msglev, "%s", format_route_entry (&rt->table[i], &gc));
	}
    }
  gc_free (&gc);
}

#elif defined(TARGET_LINUX)

bool
get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
{
  struct gc_arena gc = gc_new ();
  bool ret = false;
  FILE *fp = fopen ("/proc/net/route", "r");
  if (fp)
    {
      char line[256];
      int count = 0;
      int best_count = 0;
      unsigned int lowest_metric = ~0;
      in_addr_t best_gw = 0;
      while (fgets (line, sizeof (line), fp) != NULL)
	{
	  if (count)
	    {
	      unsigned int net_x = 0;
	      unsigned int mask_x = 0;
	      unsigned int gw_x = 0;
	      unsigned int metric = 0;
	      const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
				     &net_x,
				     &gw_x,
				     &metric,
				     &mask_x);
	      if (np == 4)
		{
		  const in_addr_t net = ntohl (net_x);
		  const in_addr_t mask = ntohl (mask_x);
		  const in_addr_t gw = ntohl (gw_x);

		  dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
			count,
			print_in_addr_t ((in_addr_t) net, 0, &gc),
			print_in_addr_t ((in_addr_t) mask, 0, &gc),
			print_in_addr_t ((in_addr_t) gw, 0, &gc),
			metric);

		  if (!net && !mask && metric < lowest_metric)
		    {
		      best_gw = gw;
		      lowest_metric = metric;
		      best_count = count;
		    }
		}
	    }
	  ++count;
	}
      fclose (fp);

      if (best_gw)
	{
	  *gateway = best_gw;
	  if (netmask)
	    {
	      *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
	    }
	  ret = true;
	}

      dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
	    print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
	    best_count,
	    (unsigned int)lowest_metric);
    }

  gc_free (&gc);
  return ret;
}

#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* all of this is taken from <net/route.h> in FreeBSD */
#define RTA_DST     0x1
#define RTA_GATEWAY 0x2
#define RTA_NETMASK 0x4

#define RTM_GET     0x4
#define RTM_VERSION 5

#define RTF_UP      0x1
#define RTF_GATEWAY 0x2

/*
 * These numbers are used by reliable protocols for determining
 * retransmission behavior and are included in the routing structure.
 */
struct rt_metrics {
        u_long  rmx_locks;      /* Kernel must leave these values alone */
        u_long  rmx_mtu;        /* MTU for this path */
        u_long  rmx_hopcount;   /* max hops expected */
        u_long  rmx_expire;     /* lifetime for route, e.g. redirect */
        u_long  rmx_recvpipe;   /* inbound delay-bandwidth product */
        u_long  rmx_sendpipe;   /* outbound delay-bandwidth product */
        u_long  rmx_ssthresh;   /* outbound gateway buffer limit */
        u_long  rmx_rtt;        /* estimated round trip time */
        u_long  rmx_rttvar;     /* estimated rtt variance */
        u_long  rmx_pksent;     /* packets sent using this route */
        u_long  rmx_filler[4];  /* will be used for T/TCP later */
};

/*
 * Structures for routing messages.
 */
struct rt_msghdr {
        u_short rtm_msglen;     /* to skip over non-understood messages */
        u_char  rtm_version;    /* future binary compatibility */
        u_char  rtm_type;       /* message type */
        u_short rtm_index;      /* index for associated ifp */
        int     rtm_flags;      /* flags, incl. kern & message, e.g. DONE */
        int     rtm_addrs;      /* bitmask identifying sockaddrs in msg */
        pid_t   rtm_pid;        /* identify sender */
        int     rtm_seq;        /* for sender to identify action */
        int     rtm_errno;      /* why failed */
        int     rtm_use;        /* from rtentry */
        u_long  rtm_inits;      /* which metrics we are initializing */
        struct  rt_metrics rtm_rmx; /* metrics themselves */
};

struct {
  struct rt_msghdr m_rtm;
  char       m_space[512];
} m_rtmsg;

#define ROUNDUP(a) \
        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))

bool
get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
{
  struct gc_arena gc = gc_new ();
  int s, seq, l, pid, rtm_addrs, i;
  struct sockaddr so_dst, so_mask;
  char *cp = m_rtmsg.m_space; 
  struct sockaddr *gate = NULL, *sa;
  struct  rt_msghdr *rtm_aux;

#define NEXTADDR(w, u) \
        if (rtm_addrs & (w)) {\
            l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
        }

#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

#define rtm m_rtmsg.m_rtm

  pid = getpid();
  seq = 0;
  rtm_addrs = RTA_DST | RTA_NETMASK;

  bzero(&so_dst, sizeof(so_dst));
  bzero(&so_mask, sizeof(so_mask));
  bzero(&rtm, sizeof(struct rt_msghdr));

  rtm.rtm_type = RTM_GET;
  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
  rtm.rtm_version = RTM_VERSION;
  rtm.rtm_seq = ++seq;
  rtm.rtm_addrs = rtm_addrs; 

  so_dst.sa_family = AF_INET;
  so_dst.sa_len = sizeof(struct sockaddr_in);
  so_mask.sa_family = AF_INET;
  so_mask.sa_len = sizeof(struct sockaddr_in);

  NEXTADDR(RTA_DST, so_dst);
  NEXTADDR(RTA_NETMASK, so_mask);

  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;

  s = socket(PF_ROUTE, SOCK_RAW, 0);

  if (write(s, (char *)&m_rtmsg, l) < 0)
    {
      warn("writing to routing socket");
      gc_free (&gc);
      close(s);
      return false;
    }

  do {
    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
                        
  close(s);

  rtm_aux = &rtm;

  cp = ((char *)(rtm_aux + 1));
  if (rtm_aux->rtm_addrs) {
    for (i = 1; i; i <<= 1)
      if (i & rtm_aux->rtm_addrs) {
	sa = (struct sockaddr *)cp;
	if (i == RTA_GATEWAY )
	  gate = sa;
	ADVANCE(cp, sa);
      }
  }
  else
    {
      gc_free (&gc);
      return false;
    }


  if (gate != NULL )
    {
      *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
#if 0
      msg (M_INFO, "gw %s",
	   print_in_addr_t ((in_addr_t) *ret, 0, &gc));
#endif

      if (netmask)
	{
	  *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
	}

      gc_free (&gc);
      return true;
    }
  else
    {
      gc_free (&gc);
      return false;
    }
}

#elif defined(TARGET_DARWIN)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* all of this is taken from <net/route.h> in Darwin */
#define RTA_DST     0x1
#define RTA_GATEWAY 0x2
#define RTA_NETMASK 0x4

#define RTM_GET     0x4
#define RTM_VERSION 5

#define RTF_UP      0x1
#define RTF_GATEWAY 0x2

/*
 * These numbers are used by reliable protocols for determining
 * retransmission behavior and are included in the routing structure.
 */
struct rt_metrics {
        u_long  rmx_locks;      /* Kernel must leave these values alone */
        u_long  rmx_mtu;        /* MTU for this path */
        u_long  rmx_hopcount;   /* max hops expected */
        u_long  rmx_expire;     /* lifetime for route, e.g. redirect */
        u_long  rmx_recvpipe;   /* inbound delay-bandwidth product */
        u_long  rmx_sendpipe;   /* outbound delay-bandwidth product */
        u_long  rmx_ssthresh;   /* outbound gateway buffer limit */
        u_long  rmx_rtt;        /* estimated round trip time */
        u_long  rmx_rttvar;     /* estimated rtt variance */
        u_long  rmx_pksent;     /* packets sent using this route */
        u_long  rmx_filler[4];  /* will be used for T/TCP later */
};

/*
 * Structures for routing messages.
 */
struct rt_msghdr {
        u_short rtm_msglen;     /* to skip over non-understood messages */
        u_char  rtm_version;    /* future binary compatibility */
        u_char  rtm_type;       /* message type */
        u_short rtm_index;      /* index for associated ifp */
        int     rtm_flags;      /* flags, incl. kern & message, e.g. DONE */
        int     rtm_addrs;      /* bitmask identifying sockaddrs in msg */
        pid_t   rtm_pid;        /* identify sender */
        int     rtm_seq;        /* for sender to identify action */
        int     rtm_errno;      /* why failed */
        int     rtm_use;        /* from rtentry */
        u_long  rtm_inits;      /* which metrics we are initializing */
        struct  rt_metrics rtm_rmx; /* metrics themselves */
};

struct {
  struct rt_msghdr m_rtm;
  char       m_space[512];
} m_rtmsg;

#define ROUNDUP(a) \
        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))

bool
get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
{
  struct gc_arena gc = gc_new ();
  int s, seq, l, pid, rtm_addrs, i;
  struct sockaddr so_dst, so_mask;
  char *cp = m_rtmsg.m_space; 
  struct sockaddr *gate = NULL, *sa;
  struct  rt_msghdr *rtm_aux;

#define NEXTADDR(w, u) \
        if (rtm_addrs & (w)) {\
            l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
        }

#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

#define rtm m_rtmsg.m_rtm

  pid = getpid();
  seq = 0;
  rtm_addrs = RTA_DST | RTA_NETMASK;

  bzero(&so_dst, sizeof(so_dst));
  bzero(&so_mask, sizeof(so_mask));
  bzero(&rtm, sizeof(struct rt_msghdr));

  rtm.rtm_type = RTM_GET;
  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
  rtm.rtm_version = RTM_VERSION;
  rtm.rtm_seq = ++seq;
  rtm.rtm_addrs = rtm_addrs; 

  so_dst.sa_family = AF_INET;
  so_dst.sa_len = sizeof(struct sockaddr_in);
  so_mask.sa_family = AF_INET;
  so_mask.sa_len = sizeof(struct sockaddr_in);

  NEXTADDR(RTA_DST, so_dst);
  NEXTADDR(RTA_NETMASK, so_mask);

  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;

  s = socket(PF_ROUTE, SOCK_RAW, 0);

  if (write(s, (char *)&m_rtmsg, l) < 0)
    {
      msg (M_WARN, "ROUTE: problem writing to routing socket");
      gc_free (&gc);
      close(s);
      return false;
    }

  do {
    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
                        
  close(s);

  rtm_aux = &rtm;

  cp = ((char *)(rtm_aux + 1));
  if (rtm_aux->rtm_addrs) {
    for (i = 1; i; i <<= 1)
      if (i & rtm_aux->rtm_addrs) {
	sa = (struct sockaddr *)cp;
	if (i == RTA_GATEWAY )
	  gate = sa;
	ADVANCE(cp, sa);
      }
  }
  else
    {
      gc_free (&gc);
      return false;
    }


  if (gate != NULL )
    {
      *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
#if 0
      msg (M_INFO, "gw %s",
	   print_in_addr_t ((in_addr_t) *ret, 0, &gc));
#endif

      if (netmask)
	{
	  *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
	}

      gc_free (&gc);
      return true;
    }
  else
    {
      gc_free (&gc);
      return false;
    }
}

#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

/* all of this is taken from <net/route.h> in OpenBSD 3.6 */
#define RTA_DST		0x1	/* destination sockaddr present */
#define RTA_GATEWAY	0x2	/* gateway sockaddr present */
#define RTA_NETMASK	0x4	/* netmask sockaddr present */

#define RTM_GET		0x4	/* Report Metrics */

#define RTM_VERSION	3	/* Up the ante and ignore older versions */

#define	RTF_UP		0x1		/* route usable */
#define	RTF_GATEWAY	0x2		/* destination is a gateway */

/*
 * Huge version for userland compatibility.
 */
struct rt_metrics {
	u_long	rmx_locks;	/* Kernel must leave these values alone */
	u_long	rmx_mtu;	/* MTU for this path */
	u_long	rmx_hopcount;	/* max hops expected */
	u_long	rmx_expire;	/* lifetime for route, e.g. redirect */
	u_long	rmx_recvpipe;	/* inbound delay-bandwidth product */
	u_long	rmx_sendpipe;	/* outbound delay-bandwidth product */
	u_long	rmx_ssthresh;	/* outbound gateway buffer limit */
	u_long	rmx_rtt;	/* estimated round trip time */
	u_long	rmx_rttvar;	/* estimated rtt variance */
	u_long	rmx_pksent;	/* packets sent using this route */
};

/*
 * Structures for routing messages.
 */
struct rt_msghdr {
	u_short	rtm_msglen;	/* to skip over non-understood messages */
	u_char	rtm_version;	/* future binary compatibility */
	u_char	rtm_type;	/* message type */
	u_short	rtm_index;	/* index for associated ifp */
	int	rtm_flags;	/* flags, incl. kern & message, e.g. DONE */
	int	rtm_addrs;	/* bitmask identifying sockaddrs in msg */
	pid_t	rtm_pid;	/* identify sender */
	int	rtm_seq;	/* for sender to identify action */
	int	rtm_errno;	/* why failed */
	int	rtm_use;	/* from rtentry */
	u_long	rtm_inits;	/* which metrics we are initializing */
	struct	rt_metrics rtm_rmx; /* metrics themselves */
};

struct {
  struct rt_msghdr m_rtm;
  char       m_space[512];
} m_rtmsg;

#define ROUNDUP(a) \
        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))

bool
get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
{
  struct gc_arena gc = gc_new ();
  int s, seq, l, rtm_addrs, i;
  pid_t pid;
  struct sockaddr so_dst, so_mask;
  char *cp = m_rtmsg.m_space; 
  struct sockaddr *gate = NULL, *sa;
  struct  rt_msghdr *rtm_aux;

#define NEXTADDR(w, u) \
        if (rtm_addrs & (w)) {\
            l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
        }

#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

#define rtm m_rtmsg.m_rtm

  pid = getpid();
  seq = 0;
  rtm_addrs = RTA_DST | RTA_NETMASK;

  bzero(&so_dst, sizeof(so_dst));
  bzero(&so_mask, sizeof(so_mask));
  bzero(&rtm, sizeof(struct rt_msghdr));

  rtm.rtm_type = RTM_GET;
  rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
  rtm.rtm_version = RTM_VERSION;
  rtm.rtm_seq = ++seq;
  rtm.rtm_addrs = rtm_addrs; 

  so_dst.sa_family = AF_INET;
  so_dst.sa_len = sizeof(struct sockaddr_in);
  so_mask.sa_family = AF_INET;
  so_mask.sa_len = sizeof(struct sockaddr_in);

  NEXTADDR(RTA_DST, so_dst);
  NEXTADDR(RTA_NETMASK, so_mask);

  rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;

  s = socket(PF_ROUTE, SOCK_RAW, 0);

  if (write(s, (char *)&m_rtmsg, l) < 0)
    {
      warn("writing to routing socket");
      gc_free (&gc);
      close(s);
      return false;
    }

  do {
    l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
                        
  close(s);

  rtm_aux = &rtm;

  cp = ((char *)(rtm_aux + 1));
  if (rtm_aux->rtm_addrs) {
    for (i = 1; i; i <<= 1)
      if (i & rtm_aux->rtm_addrs) {
	sa = (struct sockaddr *)cp;
	if (i == RTA_GATEWAY )
	  gate = sa;
	ADVANCE(cp, sa);
      }
  }
  else
    {
      gc_free (&gc);
      return false;
    }


  if (gate != NULL )
    {
      *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
#if 0
      msg (M_INFO, "gw %s",
	   print_in_addr_t ((in_addr_t) *ret, 0, &gc));
#endif

      if (netmask)
	{
	  *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
	}

      gc_free (&gc);
      return true;
    }
  else
    {
      gc_free (&gc);
      return false;
    }
}

#else

bool
get_default_gateway (in_addr_t *ret, in_addr_t *netmask)  /* PLATFORM-SPECIFIC */
{
  return false;
}

#endif

bool
netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbits)
{
  int i;
  const int addrlen = sizeof (in_addr_t) * 8;

  if ((network & netmask) == network)
    {
      for (i = 0; i <= addrlen; ++i)
	{
	  in_addr_t mask = netbits_to_netmask (i);
	  if (mask == netmask)
	    {
	      if (i == addrlen)
		*netbits = -1;
	      else
		*netbits = i;
	      return true;
	    }
	}
    }
  return false;
}

/*
 * get_bypass_addresses() is used by the redirect-gateway bypass-x
 * functions to build a route bypass to selected DHCP/DNS servers,
 * so that outgoing packets to these servers don't end up in the tunnel.
 */

#if defined(WIN32)

static void
add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr)
{
  if (test_local_addr(addr) == TLA_NONLOCAL && addr != 0 && addr != ~0)
    add_bypass_address (rb, addr);
}

static void
add_host_route_array (struct route_bypass *rb, const IP_ADDR_STRING *iplist)
{
  while (iplist)
    {
      bool succeed = false;
      const in_addr_t ip = getaddr (GETADDR_HOST_ORDER, iplist->IpAddress.String, 0, &succeed, NULL);
      if (succeed)
	{
	  add_host_route_if_nonlocal (rb, ip);
	}
      iplist = iplist->Next;
    }
}

static void
get_bypass_addresses (struct route_bypass *rb, const unsigned int flags)
{
  struct gc_arena gc = gc_new ();
  /*bool ret_bool = false;*/

  /* get full routing table */
  const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);

  /* get the route which represents the default gateway */
  const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);

  if (row)
    {
      /* get the adapter which the default gateway is associated with */
      const IP_ADAPTER_INFO *dgi = get_adapter_info (row->dwForwardIfIndex, &gc);

      /* get extra adapter info, such as DNS addresses */
      const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (row->dwForwardIfIndex, &gc);

      /* Bypass DHCP server address */
      if ((flags & RG_BYPASS_DHCP) && dgi && dgi->DhcpEnabled)
	add_host_route_array (rb, &dgi->DhcpServer);

      /* Bypass DNS server addresses */
      if ((flags & RG_BYPASS_DNS) && pai)
	add_host_route_array (rb, &pai->DnsServerList);
    }

  gc_free (&gc);
}

#else

static void
get_bypass_addresses (struct route_bypass *rb, const unsigned int flags)  /* PLATFORM-SPECIFIC */
{
}

#endif

#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)

#if defined(TARGET_LINUX)

bool
get_default_gateway_mac_addr (unsigned char *macaddr)
{
  struct ifreq *ifr, *ifend;
  in_addr_t ina, mask;
  struct ifreq ifreq;
  struct ifconf ifc;
  struct ifreq ifs[20]; // Maximum number of interfaces to scan
  int sd = -1;
  in_addr_t gwip = 0;
  bool ret = false;

  if (!get_default_gateway (&gwip, NULL))
    {
      msg (M_WARN, "GDGMA: get_default_gateway failed");
      goto err;
    }

  if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      msg (M_WARN, "GDGMA: socket() failed");
      goto err;
    }

  ifc.ifc_len = sizeof (ifs);
  ifc.ifc_req = ifs;
  if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
    {
      msg (M_WARN, "GDGMA: ioctl(SIOCGIFCONF) failed");
      goto err;
    }

  /* scan through interface list */
  ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
  for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
    {
      if (ifr->ifr_addr.sa_family == AF_INET)
	{
	  ina = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
	  strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));

	  dmsg (D_AUTO_USERID, "GDGMA: %s", ifreq.ifr_name);

	  /* check that the interface is up, and not point-to-point or loopback */
	  if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
	    {
	      dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFFLAGS(%s) failed", ifreq.ifr_name);
	      continue;
	    }

	  if ((ifreq.ifr_flags & (IFF_UP|IFF_LOOPBACK)) != IFF_UP)
	    {
	      dmsg (D_AUTO_USERID, "GDGMA: interface %s is down or loopback", ifreq.ifr_name);
	      continue;
	    }

	  /* get interface netmask and check for correct subnet */
	  if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
	    {
	      dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFNETMASK(%s) failed", ifreq.ifr_name);
	      continue;
	    }

	  mask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
	  if (((gwip ^ ina) & mask) != 0)
	    {
	      dmsg (D_AUTO_USERID, "GDGMA: gwip=0x%08x ina=0x%08x mask=0x%08x",
		    (unsigned int)gwip,
		    (unsigned int)ina,
		    (unsigned int)mask);
	      continue;
	    }
	  break;
	}
    }
  if (ifr >= ifend)
    {
      msg (M_WARN, "GDGMA: couldn't find gw interface");
      goto err;
    }

  /* now get the hardware address. */
  memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
  if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
    {
      msg (M_WARN, "GDGMA: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
      goto err;
    }

  memcpy (macaddr, &ifreq.ifr_hwaddr.sa_data, 6);
  ret = true;

 err:
  if (sd >= 0)
    close (sd);
  return ret;
}

#elif defined(WIN32)

bool
get_default_gateway_mac_addr (unsigned char *macaddr)
{
  struct gc_arena gc = gc_new ();
  const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
  in_addr_t gwip = 0;
  DWORD a_index;
  const IP_ADAPTER_INFO *ai;

  if (!get_default_gateway (&gwip, NULL))
    {
      msg (M_WARN, "GDGMA: get_default_gateway failed");
      goto err;
    }

  a_index = adapter_index_of_ip (adapters, gwip, NULL, NULL);
  ai = get_adapter (adapters, a_index);

  if (!ai)
    {
      msg (M_WARN, "GDGMA: couldn't find gw interface");
      goto err;
    }

  memcpy (macaddr, ai->Address, 6);

  gc_free (&gc);
  return true;

 err:
  gc_free (&gc);
  return false;
}

#else

bool
get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
{
  return false;
}

#endif
#endif /* AUTO_USERID */

/*
 * Test if addr is reachable via a local interface (return ILA_LOCAL),
 * or if it needs to be routed via the default gateway (return
 * ILA_NONLOCAL).  If the target platform doesn't implement this
 * function, return ILA_NOT_IMPLEMENTED.
 *
 * Used by redirect-gateway autolocal feature
 */

#if defined(WIN32)

int
test_local_addr (const in_addr_t addr)
{
  struct gc_arena gc = gc_new ();
  const in_addr_t nonlocal_netmask = 0x80000000L; /* routes with netmask <= to this are considered non-local */
  bool ret = TLA_NONLOCAL;

  /* get full routing table */
  const MIB_IPFORWARDTABLE *rt = get_windows_routing_table (&gc);
  if (rt)
    {
      int i;
      for (i = 0; i < rt->dwNumEntries; ++i)
	{
	  const MIB_IPFORWARDROW *row = &rt->table[i];
	  const in_addr_t net = ntohl (row->dwForwardDest);
	  const in_addr_t mask = ntohl (row->dwForwardMask);
	  if (mask > nonlocal_netmask && (addr & mask) == net)
	    {
	      ret = TLA_LOCAL;
	      break;
	    }
	}
    }

  gc_free (&gc);
  return ret;
}

#else


int
test_local_addr (const in_addr_t addr) /* PLATFORM-SPECIFIC */
{
  return TLA_NOT_IMPLEMENTED;
}

#endif