├── po ├── LINGUAS ├── stamp-po ├── quot.sed ├── boldquot.sed ├── POTFILES.in ├── Makevars ├── ChangeLog ├── remove-potcdate.sin ├── insert-header.sin ├── en@quot.header ├── en@boldquot.header ├── Rules-quot ├── Makefile.in.in └── nb.po ├── m4 └── .place_holder ├── src ├── mndp.h ├── mactelnet.h ├── console.h ├── Makefile.am ├── autologin.h ├── users.h ├── extra.h ├── interfaces.h ├── mtwei.h ├── console.c ├── protocol.h ├── mndp.c ├── autologin.c ├── macping.c ├── users.c ├── protocol.c ├── mtwei.c └── interfaces.c ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── build.yaml │ └── docker.yaml ├── .editorconfig ├── Makefile.am ├── config ├── Makefile.am ├── mactelnetd.init ├── mactelnetd.users └── mactelnet.spec ├── doc ├── Makefile.am ├── mndp.1 ├── macping.1 ├── mactelnet.1 └── mactelnetd.1.in ├── .clang-format ├── autogen.sh ├── .dockerignore ├── .gitignore ├── Dockerfile ├── .vscode └── c_cpp_properties.json ├── .docker └── centos7.sh ├── configure.ac ├── README.markdown └── LICENSE /po/LINGUAS: -------------------------------------------------------------------------------- 1 | nb 2 | bg 3 | -------------------------------------------------------------------------------- /po/stamp-po: -------------------------------------------------------------------------------- 1 | timestamp 2 | -------------------------------------------------------------------------------- /m4/.place_holder: -------------------------------------------------------------------------------- 1 | m4: add folder for travis build 2 | -------------------------------------------------------------------------------- /src/mndp.h: -------------------------------------------------------------------------------- 1 | #ifndef _MNDP_H 2 | #define _MNDP_H 3 | 4 | int mndp(int timeout, int batch_mode); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - **MAC Telnet version:** 2 | - **Operating system and architecture:** 3 | - **Issue** 4 | - **Log** 5 | -------------------------------------------------------------------------------- /po/quot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | 7 | [*.[ch]] 8 | indent_style = tabs 9 | insert_final_newline = true 10 | tab_width = 4 -------------------------------------------------------------------------------- /po/boldquot.sed: -------------------------------------------------------------------------------- 1 | s/"\([^"]*\)"/“\1”/g 2 | s/`\([^`']*\)'/‘\1’/g 3 | s/ '\([^`']*\)' / ‘\1’ /g 4 | s/ '\([^`']*\)'$/ ‘\1’/g 5 | s/^'\([^`']*\)' /‘\1’ /g 6 | s/“”/""/g 7 | s/“/“/g 8 | s/”/”/g 9 | s/‘/‘/g 10 | s/’/’/g 11 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | SUBDIRS = src doc config po 3 | 4 | CFLAGS = --pedantic -Wall -std=c99 -O3 5 | LDFLAGS = 6 | 7 | 8 | ACLOCAL_AMFLAGS = -I m4 9 | 10 | EXTRA_DIST = config.rpath README.markdown LICENSE 11 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | # List of source files which contain translatable strings. 2 | src/mactelnet.c 3 | src/mactelnetd.c 4 | src/macping.c 5 | src/mndp.c 6 | src/autologin.c 7 | src/interfaces.c 8 | src/protocol.c 9 | src/mtwei.c 10 | src/users.c -------------------------------------------------------------------------------- /config/Makefile.am: -------------------------------------------------------------------------------- 1 | if INSTALL_CONFIG 2 | dist_sysconf_DATA = mactelnetd.users 3 | 4 | install-exec-hook: 5 | chmod 600 $(DESTDIR)$(sysconfdir)/mactelnetd.users 6 | chown root $(DESTDIR)$(sysconfdir)/mactelnetd.users 7 | else 8 | dist_sysconf_DATA = 9 | endif -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_man_MANS = mactelnet.1 mndp.1 macping.1 2 | 3 | if BUILD_MACTELNETD 4 | dist_man_MANS += mactelnetd.1 5 | EXTRA_DIST = mactelnetd.1.in 6 | CLEANFILES = mactelnetd.1 7 | 8 | mactelnetd.1: mactelnetd.1.in 9 | $(AM_V_GEN)$(SED) -e 's|@sysconfdir[@]|$(sysconfdir)|g' mactelnetd.1.in > $@ 10 | 11 | endif -------------------------------------------------------------------------------- /po/Makevars: -------------------------------------------------------------------------------- 1 | # Makefile variables for PO directory in any package using GNU gettext. 2 | DOMAIN = $(PACKAGE) 3 | 4 | subdir = po 5 | top_builddir = .. 6 | 7 | XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ 8 | 9 | COPYRIGHT_HOLDER = Håkon Nessjøen 10 | 11 | MSGID_BUGS_ADDRESS = haakon.nessjoen@gmail.com 12 | 13 | EXTRA_LOCALE_CATEGORIES = 14 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | UseTab: Always 4 | SortIncludes: Never 5 | IndentWidth: 4 6 | TabWidth: 4 7 | ColumnLimit: "120" 8 | AllowShortBlocksOnASingleLine: Empty 9 | AllowShortCaseLabelsOnASingleLine: false 10 | AllowShortFunctionsOnASingleLine: false 11 | AllowShortIfStatementsOnASingleLine: false 12 | AllowShortLoopsOnASingleLine: false 13 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Clean All 4 | if [ "$1" = "clean" ]; then 5 | make clean 6 | rm -f aclocal.m4 compile configure install-sh \ 7 | depcomp ltmain.sh config.guess config.sub \ 8 | `find . -name Makefile.in` compile `find . -name Makefile` 9 | rm -rf autom4te.cache 10 | rm -rf src/.deps 11 | exit 12 | fi 13 | 14 | aclocal -I m4 15 | autoreconf -i 16 | automake --gnu --add-missing 17 | autoconf 18 | 19 | ./configure "$@" 20 | -------------------------------------------------------------------------------- /config/mactelnetd.init: -------------------------------------------------------------------------------- 1 | # mactelnetd - MAC-Telnet server 2 | # 3 | # The MAC-Telnet server provides telnet access via MAC addresses. 4 | # 5 | # Ubuntu upstart config: 6 | 7 | description "MAC-Telnet server" 8 | 9 | start on filesystem 10 | stop on runlevel [!2345] 11 | 12 | respawn 13 | respawn limit 10 5 14 | umask 022 15 | 16 | pre-start script 17 | test -O /etc/mactelnetd.users || { stop; exit 0; } 18 | test -x /usr/sbin/mactelnetd || { stop; exit 0; } 19 | end script 20 | 21 | exec /usr/sbin/mactelnetd -f 22 | -------------------------------------------------------------------------------- /po/ChangeLog: -------------------------------------------------------------------------------- 1 | 2016-10-10 gettextize 2 | 3 | * Makefile.in.in: New file, from gettext-0.18.1. 4 | * Rules-quot: New file, from gettext-0.18.1. 5 | * boldquot.sed: New file, from gettext-0.18.1. 6 | * en@boldquot.header: New file, from gettext-0.18.1. 7 | * en@quot.header: New file, from gettext-0.18.1. 8 | * insert-header.sin: New file, from gettext-0.18.1. 9 | * quot.sed: New file, from gettext-0.18.1. 10 | * remove-potcdate.sin: New file, from gettext-0.18.1. 11 | * POTFILES.in: New file. 12 | 13 | -------------------------------------------------------------------------------- /po/remove-potcdate.sin: -------------------------------------------------------------------------------- 1 | # Sed script that remove the POT-Creation-Date line in the header entry 2 | # from a POT file. 3 | # 4 | # The distinction between the first and the following occurrences of the 5 | # pattern is achieved by looking at the hold space. 6 | /^"POT-Creation-Date: .*"$/{ 7 | x 8 | # Test if the hold space is empty. 9 | s/P/P/ 10 | ta 11 | # Yes it was empty. First occurrence. Remove the line. 12 | g 13 | d 14 | bb 15 | :a 16 | # The hold space was nonempty. Following occurrences. Do nothing. 17 | x 18 | :b 19 | } 20 | -------------------------------------------------------------------------------- /config/mactelnetd.users: -------------------------------------------------------------------------------- 1 | # Users file for MAC-Telnetd 2 | # 3 | #################################################################### 4 | # WARNING: This file may have passwords written in plain-text. # 5 | # Make sure this file is owned and only readable by root. # 6 | #################################################################### 7 | # 8 | # Each line consists username, hash, and salt seperated by :. 9 | # If you need to add a new user, use the -a option in mactelnetd. 10 | # 11 | # Format: 12 | #username:hash:salt or username:password 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *~ 4 | ABOUT-NLS 5 | ChangeLog 6 | autom4te.cache 7 | autoscan.log 8 | autoscan-*.log 9 | aclocal.m4 10 | compile 11 | src/config.h.in 12 | depcomp 13 | install-sh 14 | missing 15 | src/stamp-h1 16 | **/*.o 17 | .deps 18 | m4/*.m4 19 | Makefile 20 | Makefile.in 21 | config.guess 22 | config.rpath 23 | config.sub 24 | config.log 25 | config.status 26 | configure 27 | configure.scan 28 | po/Makevars.template 29 | po/POTFILES 30 | po/*.gmo 31 | po/*.mo 32 | po/*.pot 33 | po/remove-potcdate.sed 34 | src/macping 35 | src/mactelnet 36 | src/mactelnetd 37 | src/mndp 38 | src/.deps/ 39 | src/config.h 40 | doc/mactelnetd.1 41 | Dockerfile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *~ 4 | ABOUT-NLS 5 | ChangeLog 6 | autom4te.cache 7 | autoscan.log 8 | autoscan-*.log 9 | aclocal.m4 10 | compile 11 | src/config.h.in 12 | depcomp 13 | install-sh 14 | missing 15 | src/stamp-h1 16 | *.o 17 | .deps 18 | m4/*.m4 19 | Makefile 20 | Makefile.in 21 | config.guess 22 | config.rpath 23 | config.sub 24 | config.log 25 | config.status 26 | configure 27 | configure.scan 28 | po/Makevars.template 29 | po/POTFILES 30 | po/*.gmo 31 | po/*.mo 32 | po/*.pot 33 | po/remove-potcdate.sed 34 | src/macping 35 | src/mactelnet 36 | src/mactelnetd 37 | src/mndp 38 | src/.deps/ 39 | src/config.h 40 | doc/mactelnetd.1 41 | .vscode/settings.json -------------------------------------------------------------------------------- /doc/mndp.1: -------------------------------------------------------------------------------- 1 | .TH MNDP 1 "February 27, 2011" 2 | .SH NAME 3 | mndp \- A tool for discovering other RouterOS or mactelnetd devices 4 | .SH SYNOPSIS 5 | .B mndp 6 | .SH DESCRIPTION 7 | This tool enables you to discover RouterOS or MAC-Telnetd enabled 8 | devices. It will display the MAC-address software version, and uptime of 9 | every device or machine it discovers. 10 | .PP 11 | To exit this application, use 12 | .B Control + C 13 | \. 14 | .SH SEE ALSO 15 | .BR mactelnet (1), 16 | .BR macping (1), 17 | .BR mactelnetd (1). 18 | .SH AUTHOR 19 | mndp was written by Håkon Nessjøen . 20 | .PP 21 | This manual page was written by Håkon Nessjøen , 22 | for the Debian project (and may be used by others). 23 | -------------------------------------------------------------------------------- /po/insert-header.sin: -------------------------------------------------------------------------------- 1 | # Sed script that inserts the file called HEADER before the header entry. 2 | # 3 | # At each occurrence of a line starting with "msgid ", we execute the following 4 | # commands. At the first occurrence, insert the file. At the following 5 | # occurrences, do nothing. The distinction between the first and the following 6 | # occurrences is achieved by looking at the hold space. 7 | /^msgid /{ 8 | x 9 | # Test if the hold space is empty. 10 | s/m/m/ 11 | ta 12 | # Yes it was empty. First occurrence. Read the file. 13 | r HEADER 14 | # Output the file's contents by reading the next line. But don't lose the 15 | # current line while doing this. 16 | g 17 | N 18 | bb 19 | :a 20 | # The hold space was nonempty. Following occurrences. Do nothing. 21 | x 22 | :b 23 | } 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm AS builder 2 | 3 | # Install build dependencies 4 | RUN apt-get update && apt-get -y install \ 5 | gettext autoconf automake libtool autopoint \ 6 | libssl-dev git libbsd-dev build-essential && \ 7 | apt-get clean && rm -rf /tmp/* /var/tmp/* 8 | 9 | # Copy everything to /src 10 | RUN mkdir /src 11 | WORKDIR /src 12 | ADD . /src/ 13 | 14 | # Build 15 | RUN ./autogen.sh --prefix=/build --sysconfdir=/config 16 | RUN make all install 17 | 18 | ## 19 | FROM debian:bookworm-slim 20 | 21 | # Install runtime dependencies 22 | RUN apt-get update && apt-get -y install gettext \ 23 | libbsd0 openssl man-db && apt-get clean && \ 24 | rm -rf /tmp/* /var/tmp/* 25 | 26 | # Copy build artifacts 27 | COPY --from=builder /build/ /usr/ 28 | COPY --from=builder /config /config 29 | 30 | CMD ["/usr/bin/mactelnet"] 31 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "LOCALEDIR=\"text\"", 10 | "USERSFILE=\"text\"" 11 | ], 12 | "macFrameworkPath": [ 13 | "/Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/System/Library/Frameworks" 14 | ], 15 | "compilerPath": "/usr/bin/clang", 16 | "cStandard": "c17", 17 | "cppStandard": "c++17", 18 | "intelliSenseMode": "macos-clang-arm64", 19 | "compilerArgs": [ 20 | "-I/opt/homebrew/opt/gettext/include", 21 | "-I/opt/homebrew/opt/openssl@3/include" 22 | ] 23 | } 24 | ], 25 | "version": 4 26 | } -------------------------------------------------------------------------------- /src/mactelnet.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #define CONNECT_TIMEOUT 2 21 | 22 | enum auth_mode_t { AUTH_MODE_MD5, AUTH_MODE_EC_SRP }; -------------------------------------------------------------------------------- /src/console.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | extern int get_terminal_size(unsigned short *width, unsigned short *height); 20 | extern int raw_term(); 21 | extern int reset_term(); 22 | extern int set_terminal_size(int fd, unsigned short width, unsigned short height); 23 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = mactelnet macping mndp 2 | 3 | if HAVE_LIBINTL 4 | LDFLAGS += -lintl 5 | endif 6 | 7 | if BUILD_MACTELNETD 8 | sbin_PROGRAMS = mactelnetd 9 | endif 10 | 11 | mactelnet_SOURCES = config.h mactelnet.c mactelnet.h protocol.c protocol.h console.c console.h interfaces.c interfaces.h mndp.c mndp.h autologin.c autologin.h extra.h utlist.h mtwei.c mtwei.h 12 | mactelnet_CFLAGS = -DFROM_MACTELNET 13 | mactelnet_LDADD = $(CRYPTO_LIBS) 14 | 15 | mactelnetd_SOURCES = config.h mactelnetd.c protocol.c protocol.h interfaces.c interfaces.h console.c console.h users.c users.h extra.h utlist.h mtwei.c mtwei.h 16 | mactelnetd_CFLAGS = -DFROM_MACTELNETD -DUSERSFILE='"$(sysconfdir)/mactelnetd.users"' 17 | mactelnetd_LDADD = $(CRYPTO_LIBS) $(COREFOUNDATION_LIBS) $(SYSTEMCONFIGURATION_LIBS) 18 | 19 | mndp_SOURCES = config.h mndp.c mndp.h protocol.c protocol.h extra.h 20 | 21 | macping_SOURCES = config.h macping.c interfaces.c interfaces.h protocol.c protocol.h extra.h utlist.h 22 | 23 | AM_CPPFLAGS = -DLOCALEDIR='"$(localedir)"' 24 | 25 | if BUILD_MACTELNETD 26 | mactelnetd-interface.o: interfaces.c 27 | $(COMPILE) $(mactelnetd_CFLAGS) $(OUTPUT_OPTION) $< 28 | endif 29 | 30 | mactelnet-interface.o: interfaces.c 31 | $(COMPILE) $(mactelnet_CFLAGS) $(OUTPUT_OPTION) $< -------------------------------------------------------------------------------- /po/en@quot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | -------------------------------------------------------------------------------- /doc/macping.1: -------------------------------------------------------------------------------- 1 | .TH MACPING 1 "February 27, 2011" 2 | .SH NAME 3 | macping \- A tool for pinging other RouterOS or mactelnetd devices 4 | .SH SYNOPSIS 5 | .B mactelnet 6 | .RI [ options ] " " < MAC-Address | hostname > 7 | .SH DESCRIPTION 8 | This tool enables you to ping other RouterOS or MAC-Telnetd enabled 9 | devices. You can ping either a hostname or a MAC address. 10 | If specified, the hostname (identity) will be looked up via MNDP discovery. 11 | .SH OPTIONS 12 | These programs follow the usual GNU command line syntax. 13 | A summary of options is included below. 14 | .TP 15 | .B \-f 16 | Fast mode, do not wait before sending next ping request. The next ping will be sent immediately when the last ping is received. This cannot be used with 17 | .B -c 0 18 | \. 19 | .TP 20 | .B \-s 21 | Specify the amount of bytes to send in each ping packet, up to ~1400 bytes. 22 | .TP 23 | .B \-c 24 | Number of packets to send before exiting. A value of 25 | .B 0 26 | means unlimited packets and the tool must be exited with Control + C. 27 | .TP 28 | .B \-h 29 | Show summary of options. 30 | .TP 31 | .B \-v 32 | Show version of program. 33 | .SH SEE ALSO 34 | .BR mndp (1), 35 | .BR mactelnet (1), 36 | .BR mactelnetd (1). 37 | .SH AUTHOR 38 | macping was written by Håkon Nessjøen . 39 | .PP 40 | This manual page was written by Håkon Nessjøen , 41 | for the Debian project (and may be used by others). 42 | -------------------------------------------------------------------------------- /po/en@boldquot.header: -------------------------------------------------------------------------------- 1 | # All this catalog "translates" are quotation characters. 2 | # The msgids must be ASCII and therefore cannot contain real quotation 3 | # characters, only substitutes like grave accent (0x60), apostrophe (0x27) 4 | # and double quote (0x22). These substitutes look strange; see 5 | # http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 6 | # 7 | # This catalog translates grave accent (0x60) and apostrophe (0x27) to 8 | # left single quotation mark (U+2018) and right single quotation mark (U+2019). 9 | # It also translates pairs of apostrophe (0x27) to 10 | # left single quotation mark (U+2018) and right single quotation mark (U+2019) 11 | # and pairs of quotation mark (0x22) to 12 | # left double quotation mark (U+201C) and right double quotation mark (U+201D). 13 | # 14 | # When output to an UTF-8 terminal, the quotation characters appear perfectly. 15 | # When output to an ISO-8859-1 terminal, the single quotation marks are 16 | # transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to 17 | # grave/acute accent (by libiconv), and the double quotation marks are 18 | # transliterated to 0x22. 19 | # When output to an ASCII terminal, the single quotation marks are 20 | # transliterated to apostrophes, and the double quotation marks are 21 | # transliterated to 0x22. 22 | # 23 | # This catalog furthermore displays the text between the quotation marks in 24 | # bold face, assuming the VT100/XTerm escape sequences. 25 | # 26 | -------------------------------------------------------------------------------- /src/autologin.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #define AUTOLOGIN_MAXSTR 100 20 | #define AUTOLOGIN_MAXPROFILES 100 21 | 22 | struct autologin_profile { 23 | char identifier[AUTOLOGIN_MAXSTR]; 24 | char username[AUTOLOGIN_MAXSTR]; 25 | char password[AUTOLOGIN_MAXSTR]; 26 | unsigned char inuse : 1; 27 | unsigned char hasUsername : 1; 28 | unsigned char hasPassword : 1; 29 | }; 30 | 31 | enum autologin_state { ALS_NONE, ALS_PREIDENTIFIER, ALS_IDENTIFIER, ALS_PREKEY, ALS_KEY, ALS_PREVALUE, ALS_VALUE }; 32 | 33 | extern struct autologin_profile login_profiles[AUTOLOGIN_MAXPROFILES]; 34 | struct autologin_profile *autologin_find_profile(char *identifier); 35 | int autologin_readfile(char *configfile); 36 | -------------------------------------------------------------------------------- /src/users.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef _USERS_H 20 | #define _USERS_H 1 21 | 22 | #define MT_CRED_LEN 100 23 | #define MT_CRED_USERLEN 32 24 | #define MT_CRED_SALTLEN 16 25 | #define MT_CRED_HASHLEN 32 26 | 27 | #if MT_CRED_LEN < MT_CRED_HASHLEN * 2 + 1 28 | #error "MT_CRED_LEN must be at least twice the length of MT_CRED_HASHLEN" 29 | #endif 30 | 31 | struct mt_credentials { 32 | char username[MT_CRED_LEN]; 33 | char password[MT_CRED_LEN]; 34 | char salt[MT_CRED_SALTLEN]; 35 | char hashed; 36 | 37 | struct mt_credentials *prev; 38 | struct mt_credentials *next; 39 | }; 40 | 41 | extern struct mt_credentials *mt_users; 42 | 43 | extern void read_userfile(); 44 | extern int add_user(const char *username, const char *password); 45 | extern struct mt_credentials *find_user(char *username); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /.docker/centos7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | 3 | # Clean the yum cache 4 | yum -y clean all 5 | yum -y clean expire-cache 6 | 7 | yum -y groupinstall 'Development Tools' 8 | 9 | gcc --version 10 | 11 | # TODO: Check for git tags, deploy RPM only on release 12 | 13 | # Prepare the RPM environment 14 | mkdir -p /tmp/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} 15 | 16 | # Macros - /etc/rpm/macros.dist 17 | cat >> /etc/rpm/macros.dist << EOF 18 | %dist .centos.el7 19 | %mactelnet 1 20 | EOF 21 | 22 | # Copy SPEC file 23 | cp MAC-Telnet/config/mactelnet.spec /tmp/rpmbuild/SPECS 24 | 25 | # Get latest version of release 26 | package_version=`grep Version MAC-Telnet/config/mactelnet.spec | awk '{print $2}'` 27 | 28 | # Create source archive for RPM Build 29 | pushd MAC-Telnet 30 | git archive --prefix=mactelnet-${package_version}/ v${package_version} | gzip > /tmp/rpmbuild/SOURCES/mactelnet-${package_version}.tar.gz 31 | popd 32 | 33 | # Build RPM 34 | rpmbuild --define '_topdir /tmp/rpmbuild' -ba -vv /tmp/rpmbuild/SPECS/mactelnet.spec 35 | 36 | # After building the RPM, try to install it 37 | # Fix the lock file error on EL7. /var/lock is a symlink to /var/run/lock 38 | mkdir -p /var/run/lock 39 | 40 | # Testing packages 41 | yum localinstall -y /tmp/rpmbuild/RPMS/x86_64/mactelnet-* 42 | 43 | 44 | # 45 | # TODO: Removed for testing rpm builder 46 | # 47 | # Source repo version 48 | #pushd MAC-Telnet 49 | # Test build 50 | #./autogen.sh 51 | #make all 52 | 53 | # 54 | # TODO: Fix packages 55 | # Extract rpm -> rpm2cpio ./mactelnet-client-0.4.4-1.centos.el7.x86_64.rpm | cpio -idmv 56 | # rpm2cpio ./mactelnet-daemon-0.4.4-1.centos.el7.x86_64.rpm | cpio -idmv 57 | # 58 | # TODO: mactelnet-client, remove man1/mactelnetd.1 59 | # TODO: mactelnet-daemon, remove man1/macping.1, man1/mactelnet.1, man1/mndp.1 60 | # 61 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build master branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | name: Configure and Build 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: false 17 | 18 | matrix: 19 | os: [ubuntu-latest, macos-13] 20 | c_compiler: [gcc-10, clang] 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: ConorMacBride/install-package@v1 25 | with: 26 | brew: gettext autoconf automake libtool openssl 27 | apt: build-essential autopoint automake autoconf libssl-dev libtool gettext libbsd-dev 28 | - name: Install compiler 29 | id: install_cc 30 | uses: rlalik/setup-cpp-compiler@master 31 | with: 32 | compiler: ${{ matrix.c_compiler }} 33 | - name: Fix gettext and openssl on macOS 34 | if: runner.os == 'macOS' 35 | id: gettext 36 | run: | 37 | export GETTEXT_PATH="$(brew --prefix gettext)" 38 | export OPENSSL_PATH="$(brew --prefix openssl)" 39 | echo "GETTEXT_PATH=${GETTEXT_PATH}" >> $GITHUB_ENV 40 | echo "LDFLAGS=-L${GETTEXT_PATH}/lib -L${OPENSSL_PATH}/lib" >> $GITHUB_ENV 41 | echo "CPPFLAGS=-I${GETTEXT_PATH}/include -I${OPENSSL_PATH}/include" >> $GITHUB_ENV 42 | echo "OPENSSL_PATH=${OPENSSL_PATH}" >> $GITHUB_ENV 43 | echo "CRYPTO_CFLAGS=-I${OPENSSL_PATH}/include" >> $GITHUB_ENV 44 | echo "CRYPTO_LIBS=-L${OPENSSL_PATH}/lib ${OPENSSL_PATH}/lib/libcrypto.3.dylib" >> $GITHUB_ENV 45 | - name: autogen/configure 46 | run: ./autogen.sh 47 | - name: configure 48 | run: ./configure CC="${{ steps.install_cc.outputs.cc }}" --prefix="${{ github.workspace }}/build" 49 | - name: make 50 | run: make all 51 | -------------------------------------------------------------------------------- /src/extra.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef _EXTRA_H 20 | #define _EXTRA_H 1 21 | 22 | #define DEBUG 0 23 | 24 | #define AUTOLOGIN_PATH "~/.mactelnet" 25 | 26 | #if defined(__APPLE__) && defined(__MACH__) 27 | #define PLATFORM_NAME "Mac OS X" 28 | 29 | #elif defined(__FreeBSD__) 30 | #define PLATFORM_NAME "FreeBSD" 31 | 32 | #elif defined(__NetBSD__) 33 | #define PLATFORM_NAME "NetBSD" 34 | 35 | #elif defined(__OpenBSD__) 36 | #define PLATFORM_NAME "OpenBSD" 37 | 38 | #elif defined(__MINT__) 39 | #define PLATFORM_NAME "FreeMiNT" 40 | 41 | #elif defined(__bsdi__) 42 | #define PLATFORM_NAME "BSD/OS" 43 | 44 | #elif defined(linux) || defined(__linux__) 45 | #define PLATFORM_NAME "Linux" 46 | 47 | #elif defined(sun) 48 | #define PLATFORM_NAME "Solaris" 49 | 50 | #elif defined(__hpux) 51 | #define PLATFORM_NAME "HPUX" 52 | 53 | #elif defined(__riscos__) 54 | #define PLATFORM_NAME "RISC OS" 55 | 56 | #elif defined(__FreeBSD_kernel__) 57 | #define PLATFORM_NAME "kFreeBSD" 58 | #else 59 | #define PLATFORM_NAME "Unknown" 60 | 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /config/mactelnet.spec: -------------------------------------------------------------------------------- 1 | Name: mactelnet 2 | Version: 0.6.1 3 | Release: 1%{?gitrev:.%{gitrev}git}%{?dist} 4 | Summary: Console tools for connecting to, and serving, devices using MikroTik RouterOS MAC-Telnet protocol. 5 | Epoch: 7 6 | 7 | License: GPL-2.0-or-later WITH Autoconf-exception-generic 8 | Group: Applications/System 9 | URL: https://salsa.debian.org/debian/mactelnet 10 | 11 | BuildRequires: automake autoconf make 12 | BuildRequires: coreutils 13 | BuildRequires: gcc 14 | BuildRequires: gettext-devel 15 | BuildRequires: openssl-devel 16 | BuildRequires: libbsd-devel 17 | BuildRequires: sed 18 | 19 | # Generated with: 20 | # git archive --prefix=%{name}-%{version}/ v%{version} | gzip > %{name}-%{version}.tar.gz 21 | # 22 | # Pre-release build tarballs should be generated with: 23 | # git archive --prefix=%{name}-%{version}/ %{gitrev} | gzip > %{name}-%{version}-%{gitrev}.tar.gz 24 | # 25 | Source0: %{name}-%{version}%{?gitrev:-%{gitrev}}.tar.gz 26 | 27 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 28 | 29 | %define _prefix /usr/local 30 | 31 | %description 32 | %{summary} 33 | 34 | %package daemon 35 | Group: Applications/System 36 | Summary: Testing 37 | 38 | BuildRequires: redhat-rpm-config 39 | 40 | %description daemon 41 | %{summary} 42 | 43 | 44 | %package client 45 | Group: Applications/System 46 | Summary: Testing 47 | 48 | BuildRequires: redhat-rpm-config 49 | 50 | %description client 51 | %{summary} 52 | 53 | 54 | %prep 55 | %setup -q 56 | 57 | %build 58 | ./autogen.sh 59 | make all 60 | 61 | %clean 62 | rm -rf $RPM_BUILD_ROOT 63 | 64 | %install 65 | make DESTDIR=$RPM_BUILD_ROOT install 66 | 67 | %files client 68 | %defattr(-,root,root,-) 69 | %attr(755,root,root) %{_bindir}/* 70 | %doc %{_prefix}/share/* 71 | 72 | %files daemon 73 | %defattr(-,root,root,-) 74 | %config(noreplace) %{_prefix}/%{_sysconfdir}/mactelnetd.users 75 | %attr(755,root,root) %{_sbindir}/* 76 | %doc %{_prefix}/share/* 77 | 78 | 79 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build: 14 | permissions: 15 | contents: read 16 | packages: write 17 | attestations: write 18 | id-token: write 19 | name: Configure and Build 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v3 26 | - name: Log in to the Container registry 27 | uses: docker/login-action@v3 28 | with: 29 | registry: ${{ env.REGISTRY }} 30 | username: ${{ github.actor }} 31 | password: ${{ secrets.GITHUB_TOKEN }} 32 | - name: Fix lowercase name 33 | id: tolower 34 | run: | 35 | echo imagename=$(echo ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_OUTPUT 36 | - name: Docker meta 37 | id: meta 38 | uses: docker/metadata-action@v5 39 | with: 40 | images: ${{ steps.tolower.outputs.imagename }} 41 | - name: Set up Docker Buildx 42 | uses: docker/setup-buildx-action@v3 43 | - name: Build and push 44 | uses: docker/build-push-action@v5 45 | id: push 46 | with: 47 | context: . 48 | platforms: linux/amd64,linux/arm64 49 | push: true 50 | tags: ${{ steps.meta.outputs.tags }} 51 | labels: ${{ steps.meta.outputs.labels }} 52 | - name: Generate artifact attestation 53 | uses: actions/attest-build-provenance@v1 54 | with: 55 | subject-name: ${{ steps.tolower.outputs.imagename }} 56 | subject-digest: ${{ steps.push.outputs.digest }} 57 | push-to-registry: true 58 | - name: Inspect 59 | run: | 60 | docker buildx imagetools inspect ${{ steps.tolower.outputs.imagename }}:${{ steps.meta.outputs.version }} -------------------------------------------------------------------------------- /doc/mactelnet.1: -------------------------------------------------------------------------------- 1 | .TH MACTELNET 1 "February 27, 2011" 2 | .SH NAME 3 | mactelnet \- A tool for telneting via MAC addresses 4 | .SH SYNOPSIS 5 | .B mactelnet 6 | .RI [ options ] " " < MAC-Address | hostname > 7 | .SH DESCRIPTION 8 | This tool enables you to telnet other RouterOS or MAC-Telnetd enabled 9 | devices. You can connect to either a hostname or a MAC address. 10 | If specified, the hostname (identity) will be looked up via MNDP discovery. 11 | .SH OPTIONS 12 | These programs follow the usual GNU command line syntax. 13 | A summary of options is included below. 14 | .TP 15 | .B \-l 16 | This will discover RouterOS or MAC-Telnetd enabled devices. It will 17 | list the MAC-address software version, and uptime of every device 18 | or machine it discovers. To exit the discovery, use 19 | .B Control + C 20 | \. 21 | .TP 22 | .B \-n 23 | Do not use broadcast packets. A tad less insecure but requires root privileges. 24 | This means that ethernet packets will have the specified mac-address as the packet 25 | destination, instead of using the ethernet broadcast address. 26 | .TP 27 | .B \-t 28 | Amount of seconds to wait for a response on each interface. If you have several network interfaces, this is the timeout value per interface. 29 | .TP 30 | .B \-u 31 | Specify username. Without this option, you will need to enter the username in a interactive prompt. 32 | .TP 33 | .B \-p 34 | Specify password. Without this option, you will need to enter the password in a interactive prompt. 35 | .TP 36 | .B \-a 37 | Specify the path of the autologin configuration file. The default path for this file is ~/.mactelnet. The format for this file is standard INI file layout. 38 | .TP 39 | .B \-A 40 | Do not use the autologin configuration file. Interactively ask for username and password. 41 | .TP 42 | .B \-q 43 | Quiet mode. 44 | .TP 45 | .B \-o 46 | Do not send client public key during the authentication phase. It will force the old MD5 (pre 6.43) algorithm to be used. 47 | .TP 48 | .B \-h 49 | Show summary of options. 50 | .TP 51 | .B \-v 52 | Show version of program. 53 | .SH SEE ALSO 54 | .BR mndp (1), 55 | .BR mactelnetd (1), 56 | .BR macping (1). 57 | .SH AUTHOR 58 | mactelnet was written by Håkon Nessjøen . 59 | .PP 60 | This manual page was written by Håkon Nessjøen , 61 | for the Debian project (and may be used by others). 62 | -------------------------------------------------------------------------------- /po/Rules-quot: -------------------------------------------------------------------------------- 1 | # This file, Rules-quot, can be copied and used freely without restrictions. 2 | # Special Makefile rules for English message catalogs with quotation marks. 3 | 4 | DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot 5 | 6 | .SUFFIXES: .insert-header .po-update-en 7 | 8 | en@quot.po-create: 9 | $(MAKE) en@quot.po-update 10 | en@boldquot.po-create: 11 | $(MAKE) en@boldquot.po-update 12 | 13 | en@quot.po-update: en@quot.po-update-en 14 | en@boldquot.po-update: en@boldquot.po-update-en 15 | 16 | .insert-header.po-update-en: 17 | @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ 18 | if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ 19 | tmpdir=`pwd`; \ 20 | echo "$$lang:"; \ 21 | ll=`echo $$lang | sed -e 's/@.*//'`; \ 22 | LC_ALL=C; export LC_ALL; \ 23 | cd $(srcdir); \ 24 | if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$lang -o - 2>/dev/null \ 25 | | $(SED) -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | \ 26 | { case `$(MSGFILTER) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ 27 | '' | 0.[0-9] | 0.[0-9].* | 0.1[0-8] | 0.1[0-8].*) \ 28 | $(MSGFILTER) $(SED) -f `echo $$lang | sed -e 's/.*@//'`.sed \ 29 | ;; \ 30 | *) \ 31 | $(MSGFILTER) `echo $$lang | sed -e 's/.*@//'` \ 32 | ;; \ 33 | esac } 2>/dev/null > $$tmpdir/$$lang.new.po \ 34 | ; then \ 35 | if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ 36 | rm -f $$tmpdir/$$lang.new.po; \ 37 | else \ 38 | if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ 39 | :; \ 40 | else \ 41 | echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ 42 | exit 1; \ 43 | fi; \ 44 | fi; \ 45 | else \ 46 | echo "creation of $$lang.po failed!" 1>&2; \ 47 | rm -f $$tmpdir/$$lang.new.po; \ 48 | fi 49 | 50 | en@quot.insert-header: insert-header.sin 51 | sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header 52 | 53 | en@boldquot.insert-header: insert-header.sin 54 | sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header 55 | 56 | mostlyclean: mostlyclean-quot 57 | mostlyclean-quot: 58 | rm -f *.insert-header 59 | -------------------------------------------------------------------------------- /src/interfaces.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef _INTERFACES_H 20 | #define _INTERFACES_H 1 21 | 22 | #define MAX_INTERFACES 32 23 | 24 | struct net_interface { 25 | char name[256]; 26 | unsigned char ipv4_addr[IPV4_ALEN]; 27 | unsigned char mac_addr[ETH_ALEN]; 28 | unsigned char bcast_addr[IPV4_ALEN]; 29 | 30 | /* used by mactelnetd */ 31 | int socketfd; 32 | 33 | #ifdef __linux__ 34 | int ifindex; 35 | #endif 36 | int has_mac; 37 | int in_use; 38 | struct net_interface *prev; 39 | struct net_interface *next; 40 | }; 41 | 42 | extern int should_refresh_interfaces(); 43 | 44 | #if defined(__linux__) && defined(FROM_MACTELNETD) && defined(HAVE_LINUX_NETLINK_H) 45 | extern int get_netlink_fd(); 46 | extern void read_netlink(int fd); 47 | #endif 48 | 49 | #if defined(__APPLE__) && defined(FROM_MACTELNETD) 50 | extern void *init_network_watcher_thread(void *arg); 51 | extern void init_network_watcher(); 52 | #endif 53 | 54 | extern int net_get_interfaces(struct net_interface **interfaces); 55 | extern struct net_interface *net_get_interface_ptr(struct net_interface **interfaces, char *name, int create); 56 | extern int net_init_raw_socket(); 57 | extern int net_send_udp(const int socket, struct net_interface *interface, const unsigned char *sourcemac, 58 | const unsigned char *destmac, const struct in_addr *sourceip, const int sourceport, 59 | const struct in_addr *destip, const int destport, const unsigned char *data, const int datalen); 60 | extern unsigned short in_cksum(unsigned short *addr, int len); 61 | #endif 62 | -------------------------------------------------------------------------------- /src/mtwei.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2022, Yandex 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /* Define the state of the EC-SRP Algorithm. */ 27 | typedef struct mtwei_state_s { 28 | BN_CTX *ctx; /* BN context for temporaries */ 29 | EC_GROUP *curve25519; /* Elliptic curve parameters */ 30 | EC_POINT *g; /* Curve25519 generator point */ 31 | BIGNUM *order; /* Curve25519 order */ 32 | BIGNUM *w2m, *m2w; /* Weierstrass-to-Montgomery and back conversion constants */ 33 | BIGNUM *mod; /* Curve25519 arithmetic modulus */ 34 | } mtwei_state_t; 35 | 36 | /* Initialize the algorithm. */ 37 | void mtwei_init(mtwei_state_t *state); 38 | 39 | /* Generate a keypair, optionally entangled with a validator. */ 40 | BIGNUM *mtwei_keygen(mtwei_state_t *state, uint8_t *pubkey_out, uint8_t *validator); 41 | 42 | /* Use SHA256 to generate an SRP identifier. */ 43 | void mtwei_id(const char *username, const char *password, const unsigned char *salt, uint8_t *validator_out); 44 | 45 | /* Run EC-SRP cryptography on the client and generate the response. */ 46 | void mtwei_docrypto(mtwei_state_t *state, BIGNUM *privkey, const uint8_t *server_key, const uint8_t *client_key, 47 | uint8_t *validator, uint8_t *buf_out); 48 | 49 | /* Run EC-SRP cryptography on the server and predict the response. */ 50 | void mtwei_docryptos(mtwei_state_t *state, BIGNUM *privkey, const uint8_t *client_key, const uint8_t *server_key, 51 | uint8_t *validator, uint8_t *buf_out); 52 | 53 | #define MTWEI_PUBKEY_LEN 33 54 | #define MTWEI_VALIDATOR_LEN 32 55 | -------------------------------------------------------------------------------- /src/console.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct termios orig_term; 28 | 29 | int raw_term() { 30 | struct termios new; 31 | 32 | if (tcgetattr(STDIN_FILENO, &orig_term) < 0) { 33 | perror("tcgetattr"); 34 | return -1; 35 | } 36 | 37 | memcpy(&new, &orig_term, sizeof(struct termios)); 38 | 39 | /* raw mode, from tcsetattr man page */ 40 | new.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 41 | new.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 42 | new.c_cflag &= ~(CSIZE | PARENB); 43 | new.c_cflag |= CS8; 44 | 45 | if (tcsetattr(STDIN_FILENO, TCSANOW, &new) < 0) { 46 | perror("tcsetattr"); 47 | return -1; 48 | } 49 | return 0; 50 | } 51 | 52 | int reset_term() { 53 | if (tcsetattr(STDIN_FILENO, TCSANOW, &orig_term) < 0) { 54 | perror("tcsetattr"); 55 | return -1; 56 | } 57 | return 0; 58 | } 59 | 60 | int get_terminal_size(unsigned short *width, unsigned short *height) { 61 | struct winsize ws; 62 | 63 | if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != 0) { 64 | perror("TIOCGWINSZ"); 65 | return -1; 66 | } 67 | 68 | *width = ws.ws_col; 69 | *height = ws.ws_row; 70 | 71 | return 0; 72 | } 73 | 74 | int set_terminal_size(int fd, unsigned short width, unsigned short height) { 75 | struct winsize ws; 76 | 77 | ws.ws_col = width; 78 | ws.ws_row = height; 79 | 80 | if (ioctl(fd, TIOCSWINSZ, &ws) != 0) { 81 | perror("TIOCSWINSZ"); 82 | return -1; 83 | } 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /doc/mactelnetd.1.in: -------------------------------------------------------------------------------- 1 | .TH MACTELNETD 1 "February 27, 2011" 2 | .SH NAME 3 | mactelnetd \- Telnet daemon for MAC-address connections 4 | .SH SYNOPSIS 5 | .B mactelnetd 6 | .RI [ options ] 7 | .SH DESCRIPTION 8 | This daemon listens for telnet connections from Mikrotik RouterOS devices or mactelnet clients 9 | on the same physical network. It also announces it's hostname via the MNDP protocol every minute. 10 | .SH OPTIONS 11 | These programs follow the usual GNU command line syntax. 12 | A summary of options is included below. 13 | .TP 14 | .B \-n 15 | Do not use broadcast packets. A tad less insecure. 16 | This means that ethernet packets will have the mac-address of the client as the packet 17 | destination, instead of using the ethernet broadcast address. 18 | .TP 19 | .B \-o 20 | Use the older MD5 based authentication. This is less secure, and also requires your userfile to have the passwords in plaintext format. If you are running the server with this parameter, you cannot add users using the 21 | .B \-a 22 | option. 23 | .TP 24 | .B \-a 25 | Add a new user. The user should be an existing user in your system. This can be done without restarting your mactelnetd server as it re-reads the user file for each authentication attempt. You will be prompted for the username and password, or you can use one of the following options to specify them on the command line: 26 | .RS 27 | .TP 28 | .B \-u \fIusername\fR 29 | You can specify the new username to add on the command line using this option. If this is not used, you will be prompted for the username. 30 | .TP 31 | .B \-p \fIpassword\fR 32 | You can specify the new password for the new user to add on the command line using this option. If this is not used, you will be prompted for the password. 33 | .RE 34 | .TP 35 | .B \-d \fIusername\fR 36 | Delete the specified user. 37 | .TP 38 | .B \-l 39 | List the available users in the \fI@sysconfdir@/mactelnetd.users\fR file. 40 | .TP 41 | .B \-h 42 | Show summary of options. 43 | .TP 44 | .B \-v 45 | Show version of program. 46 | .SH FILES 47 | .TP 48 | .B @sysconfdir@/mactelnetd.users 49 | This file contains a line separated list of users that will have 50 | access to your machine. Usernames and passwords are separated 51 | by colon. This file is read each time a user connects. 52 | .SH SEE ALSO 53 | .BR mndp (1), 54 | .BR mactelnet (1), 55 | .BR macping (1). 56 | .SH AUTHOR 57 | mactelnetd was written by Håkon Nessjøen . 58 | .PP 59 | This manual page was written by Håkon Nessjøen , 60 | for the Debian project (and may be used by others). 61 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.71]) 5 | AC_INIT([mactelnet],[0.6.1],[haakon.nessjoen@gmail.com]) 6 | AM_INIT_AUTOMAKE([foreign]) 7 | AC_CONFIG_SRCDIR([src/mactelnet.c]) 8 | AC_CONFIG_HEADERS([src/config.h]) 9 | 10 | AC_USE_SYSTEM_EXTENSIONS 11 | 12 | # Checks for programs. 13 | AC_PROG_CC 14 | AM_PROG_CC_C_O 15 | AC_PROG_INSTALL 16 | 17 | # Checks for libraries. 18 | AM_GNU_GETTEXT([external]) 19 | AM_GNU_GETTEXT_VERSION([0.19]) 20 | 21 | if test "x$GMSGFMT" = "x:"; then 22 | AC_MSG_ERROR([GNU gettext command line tools are required but not found.]) 23 | fi 24 | 25 | AC_CHECK_HEADER([CoreFoundation/CoreFoundation.h], 26 | [ 27 | have_corefoundation=true 28 | AC_SUBST([COREFOUNDATION_LIBS], ["-framework CoreFoundation"]) 29 | ], [have_corefoundation=false] 30 | ) 31 | AM_CONDITIONAL(HAVE_COREFOUNDATION, $have_corefoundation) 32 | 33 | AC_CHECK_HEADER([SystemConfiguration/SystemConfiguration.h], 34 | [ 35 | have_systemconfiguration=true 36 | AC_SUBST([SYSTEMCONFIGURATION_LIBS], ["-framework SystemConfiguration"]) 37 | ], [have_systemconfiguration=false] 38 | ) 39 | AM_CONDITIONAL(HAVE_SYSTEMCONFIGURATION, $have_systemconfiguration) 40 | 41 | AC_CHECK_LIB([rt], [nanosleep]) 42 | 43 | # Use pkg-config to check for libcrypto, if available 44 | # Otherwise, fall back to AC_SEARCH_LIBS 45 | m4_ifdef([PKG_PROG_PKG_CONFIG], [ 46 | # pkg-config is installed, use it to check for libcrypto 47 | PKG_PROG_PKG_CONFIG 48 | PKG_CHECK_MODULES([CRYPTO], [libcrypto >= 1.1.0], [], [ 49 | AC_SEARCH_LIBS([EVP_MD_CTX_new], [crypto], [], [AC_MSG_FAILURE([can't find openssl >= 1.1.0 crypto lib])]) 50 | ]) 51 | ], [ 52 | AC_SEARCH_LIBS([EVP_MD_CTX_new], [crypto], [], [AC_MSG_FAILURE([can't find openssl >= 1.1.0 crypto lib])]) 53 | ]) 54 | 55 | AC_ARG_WITH([config], 56 | [AS_HELP_STRING([--without-config], [don't install default config file])], 57 | [enable_config=$withval], [enable_config=yes]) 58 | 59 | AC_ARG_WITH([mactelnetd], 60 | [AS_HELP_STRING([--without-mactelnetd], [don't install mactelnetd binary])], 61 | [enable_mactelnetd=$withval], [enable_mactelnetd=yes]) 62 | 63 | AM_CONDITIONAL([INSTALL_CONFIG], [test x"$enable_config" != "xno" && test x"$enable_mactelnetd" != "xno"]) 64 | AM_CONDITIONAL([BUILD_MACTELNETD], [test x"$enable_mactelnetd" != "xno"]) 65 | 66 | # Checks for header files. 67 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h sys/random.h float.h libintl.h locale.h linux/netlink.h netinet/in.h paths.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h termios.h unistd.h utmp.h utmpx.h]) 68 | 69 | dnl check for readpassphrase. If none is found, we use getpass (with a warning) 70 | AC_CHECK_HEADER([readpassphrase.h], 71 | [READPASSPHRASE=native], 72 | AC_CHECK_HEADER([bsd/readpassphrase.h], 73 | [READPASSPHRASE=bsd], 74 | [AC_MSG_WARN([falling back to obsoleted getpass(3)])])) 75 | 76 | AS_IF([test "x$READPASSPHRASE" = "xnative"],[ 77 | AC_DEFINE([HAVE_READPASSPHRASE], [1], [Enable readpassphrase])]) 78 | 79 | AS_IF([test "x$READPASSPHRASE" = "xbsd"],[ 80 | AC_DEFINE([HAVE_BSDREADPASSPHRASE], [1], [Enable bsdreadpassphrase]) 81 | AC_SEARCH_LIBS([readpassphrase], [bsd], [], [AC_MSG_ERROR([library for bsd/readpassphrase.h not found])])]) 82 | 83 | # Check if the target platform is macOS 84 | case "$host_os" in 85 | darwin*) 86 | AC_CHECK_LIB([pthread], [pthread_create]) 87 | 88 | AC_CHECK_LIB([intl], [libintl_gettext], [HAVE_LIBINTL=yes], [HAVE_LIBINTL=no]) 89 | 90 | if test "$ac_cv_lib_pthread_pthread_create" = "yes"; then 91 | AC_SUBST([PTHREAD_LIBS], ["-lpthread"]) 92 | else 93 | AC_MSG_ERROR([pthreads library not found]) 94 | fi 95 | ;; 96 | *) 97 | ;; 98 | esac 99 | 100 | AM_CONDITIONAL([HAVE_LIBINTL], [test "x$HAVE_LIBINTL" = "xyes"]) 101 | 102 | # Checks for typedefs, structures, and compiler characteristics. 103 | AC_TYPE_PID_T 104 | AC_TYPE_SIZE_T 105 | 106 | # Checks for library functions. 107 | AC_FUNC_CHOWN 108 | AC_FUNC_FORK 109 | AC_FUNC_MALLOC 110 | AC_FUNC_STRNLEN 111 | AC_CHECK_FUNCS([getrandom arc4random alarm bzero clock_gettime getpass gettimeofday inet_ntoa memset select setenv setlocale socket strcasecmp strerror strncasecmp sysinfo uname updwtmp updwtmpx]) 112 | 113 | AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile config/Makefile po/Makefile.in]) 114 | AC_OUTPUT 115 | -------------------------------------------------------------------------------- /src/protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef _MACTELNET_H 20 | #define _MACTELNET_H 1 21 | 22 | #define MT_HEADER_LEN 22 23 | #define MT_CPHEADER_LEN 9 24 | 25 | #define MT_PACKET_LEN 1500 26 | 27 | #define MT_MACTELNET_PORT 20561 28 | 29 | #define MT_MNDP_PORT 5678 30 | #define MT_MNDP_MAX_STRING_SIZE 128 31 | #define MT_MNDP_BROADCAST_INTERVAL 60 32 | 33 | #define MT_MNDP_TIMEOUT 5 34 | #define MT_MNDP_LONGTIMEOUT 120 35 | 36 | #define MT_SOFTID_MACTELNET "MAC-Telnet" 37 | 38 | #ifndef ETH_ALEN 39 | #define ETH_ALEN 6 40 | #endif 41 | #ifndef IPV4_ALEN 42 | #define IPV4_ALEN 4 43 | #endif 44 | 45 | /* Packet type */ 46 | enum mt_ptype { 47 | MT_PTYPE_SESSIONSTART, 48 | MT_PTYPE_DATA, 49 | MT_PTYPE_ACK, 50 | MT_PTYPE_PING = 4, 51 | MT_PTYPE_PONG, 52 | MT_PTYPE_END = 255 53 | }; 54 | 55 | /* Control packet type */ 56 | enum mt_cptype { 57 | MT_CPTYPE_BEGINAUTH, 58 | MT_CPTYPE_PASSSALT, 59 | MT_CPTYPE_PASSWORD, 60 | MT_CPTYPE_USERNAME, 61 | MT_CPTYPE_TERM_TYPE, 62 | MT_CPTYPE_TERM_WIDTH, 63 | MT_CPTYPE_TERM_HEIGHT, 64 | MT_CPTYPE_PACKET_ERROR, 65 | MT_CPTYPE_END_AUTH = 9, 66 | /* Internal CPTYPE, not part of protocol */ 67 | MT_CPTYPE_PLAINDATA = -1 68 | }; 69 | 70 | /* MNDP attribute type */ 71 | enum mt_mndp_attrtype { 72 | MT_MNDPTYPE_ADDRESS = 0x0001, 73 | MT_MNDPTYPE_IDENTITY = 0x0005, 74 | MT_MNDPTYPE_VERSION = 0x0007, 75 | MT_MNDPTYPE_PLATFORM = 0x0008, 76 | MT_MNDPTYPE_TIMESTAMP = 0x000a, 77 | MT_MNDPTYPE_SOFTID = 0x000b, 78 | MT_MNDPTYPE_HARDWARE = 0x000c, 79 | MT_MNDPTYPE_IFNAME = 0x0010 80 | }; 81 | 82 | /* MNDP packet header */ 83 | struct mt_mndp_hdr { 84 | unsigned char version; 85 | unsigned char ttl; 86 | unsigned short cksum; 87 | }; 88 | 89 | struct mt_mactelnet_hdr { 90 | unsigned char ver; 91 | enum mt_ptype ptype; 92 | unsigned char clienttype[2]; 93 | unsigned char srcaddr[6]; 94 | unsigned char dstaddr[6]; 95 | unsigned short seskey; 96 | unsigned int counter; 97 | unsigned char *data; 98 | }; 99 | 100 | struct mt_mactelnet_control_hdr { 101 | enum mt_cptype cptype; 102 | unsigned int length; 103 | unsigned char *data; 104 | }; 105 | 106 | /* TODO: Add all the other information obtainable from mndp */ 107 | struct mt_mndp_info { 108 | struct mt_mndp_hdr header; 109 | unsigned char address[ETH_ALEN]; 110 | char identity[MT_MNDP_MAX_STRING_SIZE]; 111 | char version[MT_MNDP_MAX_STRING_SIZE]; 112 | char platform[MT_MNDP_MAX_STRING_SIZE]; 113 | char hardware[MT_MNDP_MAX_STRING_SIZE]; 114 | char softid[MT_MNDP_MAX_STRING_SIZE]; 115 | char ifname[MT_MNDP_MAX_STRING_SIZE]; 116 | unsigned int uptime; 117 | }; 118 | 119 | struct mt_packet { 120 | int size; 121 | unsigned char data[MT_PACKET_LEN]; 122 | }; 123 | 124 | /* MacTelnet/Winbox packets */ 125 | extern int init_packet(struct mt_packet *packet, enum mt_ptype ptype, unsigned char *srcmac, unsigned char *dstmac, 126 | unsigned short sessionkey, unsigned int counter); 127 | extern int add_control_packet(struct mt_packet *packet, enum mt_cptype cptype, void *cpdata, unsigned short data_len); 128 | extern void parse_packet(unsigned char *data, struct mt_mactelnet_hdr *pkthdr); 129 | extern int parse_control_packet(unsigned char *data, unsigned short data_len, struct mt_mactelnet_control_hdr *cpkthdr); 130 | 131 | /* MAC-Ping packets */ 132 | int init_pingpacket(struct mt_packet *packet, unsigned char *srcmac, unsigned char *dstmac); 133 | int init_pongpacket(struct mt_packet *packet, unsigned char *srcmac, unsigned char *dstmac); 134 | int add_packetdata(struct mt_packet *packet, unsigned char *data, unsigned short length); 135 | 136 | /* MNDP packets */ 137 | extern int mndp_init_packet(struct mt_packet *packet, unsigned char version, unsigned char ttl); 138 | extern int mndp_add_attribute(struct mt_packet *packet, enum mt_mndp_attrtype attrtype, void *attrdata, 139 | unsigned short data_len); 140 | 141 | extern struct mt_mndp_info *parse_mndp(const unsigned char *data, const int packet_len); 142 | int query_mndp(const char *identity, unsigned char *mac); 143 | int query_mndp_or_mac(char *address, unsigned char *dstmac, int verbose); 144 | 145 | /* Number of milliseconds between each retransmission */ 146 | #define MAX_RETRANSMIT_INTERVALS 9 147 | static const int retransmit_intervals[MAX_RETRANSMIT_INTERVALS] = {15, 20, 30, 50, 90, 170, 330, 660, 1000}; 148 | 149 | /* Control packet magic header */ 150 | static const unsigned char mt_mactelnet_cpmagic[4] = {0x56, 0x34, 0x12, 0xff}; 151 | static const unsigned char mt_mactelnet_clienttype[2] = {0x00, 0x15}; 152 | 153 | /* Must be initialized by application */ 154 | extern unsigned char mt_direction_fromserver; 155 | 156 | /* Debugging stuff */ 157 | #if defined(DEBUG_PROTO) 158 | #ifndef hexdump_defined 159 | void hexdump(const char *title, const void *buf, unsigned short len) { 160 | int i; 161 | unsigned char *data = (unsigned char *)buf; 162 | 163 | fprintf(stderr, "%s:\n", title); 164 | for (i = 0; i < len; i++) { 165 | if (!(i & 0xf)) { 166 | fprintf(stderr, "%04x:", i); 167 | } 168 | fprintf(stderr, " %02x", data[i]); 169 | if (!(~i & 0xf) || i == len - 1) { 170 | fprintf(stderr, "\n"); 171 | } 172 | } 173 | } 174 | #define HEXDUMP(title, buf, len) hexdump(title, buf, len) 175 | #define hexdump_defined 176 | #else 177 | #define HEXDUMP(title, buf, len) 178 | #endif 179 | #endif 180 | 181 | #endif 182 | -------------------------------------------------------------------------------- /src/mndp.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #if defined(__FreeBSD__) || defined(__APPLE__) 27 | #include 28 | #include 29 | #include 30 | #else 31 | #include 32 | #endif 33 | #include 34 | #include 35 | 36 | #include "config.h" 37 | #include "protocol.h" 38 | #include "extra.h" 39 | 40 | #define _(STRING) gettext(STRING) 41 | 42 | char *ether_ntoa_z(const struct ether_addr *addr); 43 | 44 | static void sig_term(int signo) { 45 | exit(0); 46 | } 47 | 48 | /* This file is also used for the -l option in mactelnet */ 49 | #ifndef FROM_MACTELNET 50 | 51 | /* Protocol data direction, not used here, but obligatory for protocol.c */ 52 | unsigned char mt_direction_fromserver = 0; 53 | 54 | int main(int argc, char **argv) { 55 | int batch_mode = 0; 56 | #else 57 | 58 | void sig_alarm(int signo) { 59 | exit(0); 60 | } 61 | 62 | int mndp(int timeout, int batch_mode) { 63 | #endif 64 | int sock, result; 65 | struct sockaddr_in si_me, si_remote; 66 | unsigned char buff[MT_PACKET_LEN]; 67 | 68 | #ifdef FROM_MACTELNET 69 | /* mactelnet.c has this set to 1 */ 70 | mt_direction_fromserver = 0; 71 | signal(SIGALRM, sig_alarm); 72 | #endif 73 | // Add support for ctrl+c inside docker containers 74 | signal(SIGTERM, sig_term); 75 | signal(SIGINT, sig_term); 76 | 77 | setlocale(LC_ALL, ""); 78 | bindtextdomain(PACKAGE, LOCALEDIR); 79 | textdomain(PACKAGE); 80 | 81 | /* Open a UDP socket handle */ 82 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 83 | 84 | /* Set initialize address/port */ 85 | memset((char *)&si_me, 0, sizeof(si_me)); 86 | si_me.sin_family = AF_INET; 87 | si_me.sin_port = htons(MT_MNDP_PORT); 88 | si_me.sin_addr.s_addr = htonl(INADDR_ANY); 89 | 90 | int optval = 1; 91 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 92 | 93 | /* Bind to specified address/port */ 94 | if (bind(sock, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) { 95 | fprintf(stderr, _("Error binding to %s:%d\n"), inet_ntoa(si_me.sin_addr), MT_MNDP_PORT); 96 | return 1; 97 | } 98 | 99 | /* Write informative message to STDERR to make it easier to use the output in simple scripts */ 100 | fprintf(stderr, _("Searching for MikroTik routers... Abort with CTRL+C.\n")); 101 | 102 | /* Set the socket to allow sending broadcast packets */ 103 | if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { 104 | fprintf(stderr, _("Unable to send broadcast packets: Operating in receive only mode.\n")); 105 | } else { 106 | /* Request routers identify themselves */ 107 | unsigned int message = 0; 108 | 109 | memset((char *)&si_remote, 0, sizeof(si_remote)); 110 | si_remote.sin_family = AF_INET; 111 | si_remote.sin_port = htons(MT_MNDP_PORT); 112 | si_remote.sin_addr.s_addr = htonl(INADDR_BROADCAST); 113 | if (sendto(sock, &message, sizeof(message), 0, (struct sockaddr *)&si_remote, sizeof(si_remote)) == -1) { 114 | fprintf(stderr, _("Unable to send broadcast packet: Operating in receive only mode.\n")); 115 | } 116 | } 117 | 118 | if (batch_mode) { 119 | printf("%s\n", "MAC-Address,Identity,Platform,Version,Hardware,Uptime,Softid,Ifname,IP"); 120 | } else { 121 | printf("\n\E[1m%-15s %-17s %s\E[m\n", _("IP"), _("MAC-Address"), 122 | _("Identity (platform version hardware) uptime")); 123 | } 124 | #ifdef FROM_MACTELNET 125 | if (timeout > 0) { 126 | alarm(timeout); 127 | } 128 | #endif 129 | 130 | while (1) { 131 | struct mt_mndp_info *packet; 132 | struct sockaddr_in addr; 133 | socklen_t addrlen = sizeof(addr); 134 | char ipstr[INET_ADDRSTRLEN]; 135 | 136 | memset(&addr, 0, addrlen); 137 | 138 | /* Wait for a UDP packet */ 139 | result = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr *)&addr, &addrlen); 140 | if (result < 0) { 141 | fprintf(stderr, _("An error occurred. aborting\n")); 142 | exit(1); 143 | } 144 | 145 | /* Parse MNDP packet */ 146 | packet = parse_mndp(buff, result); 147 | 148 | if (packet != NULL && !batch_mode) { 149 | /* Print it */ 150 | printf("%-15s ", inet_ntop(addr.sin_family, &addr.sin_addr, ipstr, sizeof ipstr)); 151 | printf("%-17s %s", ether_ntoa_z((struct ether_addr *)packet->address), packet->identity); 152 | if (packet->platform[0] != 0) { 153 | printf(" (%s %s %s)", packet->platform, packet->version, packet->hardware); 154 | } 155 | if (packet->uptime > 0) { 156 | printf(_(" up %d days %d hours"), packet->uptime / 86400, packet->uptime % 86400 / 3600); 157 | } 158 | if (packet->softid[0] != 0) { 159 | printf(" %s", packet->softid); 160 | } 161 | if (packet->ifname[0] != 0) { 162 | printf(" %s", packet->ifname); 163 | } 164 | putchar('\n'); 165 | } else if (packet != NULL) { 166 | /* Print it */ 167 | printf("'%s','%s',", ether_ntoa_z((struct ether_addr *)packet->address), packet->identity); 168 | printf("'%s','%s','%s',", packet->platform, packet->version, packet->hardware); 169 | printf("'%d','%s','%s'", packet->uptime, packet->softid, packet->ifname); 170 | printf(",'%s'", inet_ntop(addr.sin_family, &addr.sin_addr, ipstr, sizeof ipstr)); 171 | putchar('\n'); 172 | fflush(stdout); 173 | } 174 | } 175 | 176 | /* We'll never get here.. */ 177 | return 0; 178 | } 179 | 180 | char *ether_ntoa_z(const struct ether_addr *addr) { 181 | static char buf[18]; /* 12 digits + 5 colons + null terminator */ 182 | sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", addr->ether_addr_octet[0], addr->ether_addr_octet[1], 183 | addr->ether_addr_octet[2], addr->ether_addr_octet[3], addr->ether_addr_octet[4], addr->ether_addr_octet[5]); 184 | return buf; 185 | } 186 | -------------------------------------------------------------------------------- /src/autologin.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "config.h" 28 | #include "autologin.h" 29 | #include "extra.h" 30 | 31 | #define _(STRING) gettext(STRING) 32 | 33 | struct autologin_profile login_profiles[AUTOLOGIN_MAXPROFILES]; 34 | 35 | struct autologin_profile *autologin_find_profile(char *identifier) { 36 | int i; 37 | struct autologin_profile *default_profile = NULL; 38 | 39 | if (strlen(identifier) == 0) 40 | return NULL; 41 | 42 | for (i = 0; i < AUTOLOGIN_MAXPROFILES; ++i) { 43 | if (login_profiles[i].inuse && strcasecmp(identifier, login_profiles[i].identifier) == 0) { 44 | return &login_profiles[i]; 45 | } 46 | if (login_profiles[i].inuse && strcasecmp("default", login_profiles[i].identifier) == 0) { 47 | default_profile = &login_profiles[i]; 48 | } 49 | } 50 | return default_profile; 51 | } 52 | 53 | static char *tilde_to_path(char *path) { 54 | char *homepath; 55 | if (*path == '~' && (homepath = getenv("HOME"))) { 56 | static char newpath[256]; 57 | memset(newpath, 0, sizeof(newpath)); 58 | strncpy(newpath, homepath, sizeof(newpath) - 1); 59 | /* strncat is confusing, try not to overflow */ 60 | strncat(newpath, path + 1, sizeof(newpath) - strlen(newpath) - 1); 61 | return newpath; 62 | } 63 | return path; 64 | } 65 | 66 | int autologin_readfile(char *configfile) { 67 | FILE *fp; 68 | char c; 69 | int i = -1; 70 | char *file_to_read; 71 | char key[AUTOLOGIN_MAXSTR]; 72 | char *p = key; 73 | char value[AUTOLOGIN_MAXSTR]; 74 | int line_counter = 1; 75 | enum autologin_state state = ALS_NONE; 76 | 77 | memset(login_profiles, 0, sizeof(login_profiles)); 78 | 79 | /* Convert ~/path to /home/username/path */ 80 | file_to_read = tilde_to_path(configfile); 81 | 82 | fp = fopen(file_to_read, "r"); 83 | if (!fp) { 84 | if (strcmp(configfile, AUTOLOGIN_PATH) == 0) { 85 | /* Silent ignore? */ 86 | } else { 87 | fprintf(stderr, _("Error opening autologin file %s: %s\n"), file_to_read, strerror(errno)); 88 | } 89 | return 0; 90 | } 91 | while ((c = fgetc(fp)) && !feof(fp)) { 92 | if (c == '#') { 93 | while ((c = fgetc(fp)) != '\n' && !feof(fp)) 94 | ; 95 | } 96 | 97 | switch (state) { 98 | case ALS_PREIDENTIFIER: 99 | i++; 100 | if (i == AUTOLOGIN_MAXPROFILES) { 101 | goto done; 102 | } 103 | p = login_profiles[i].identifier; 104 | state++; 105 | break; 106 | 107 | case ALS_PREKEY: 108 | memset(key, 0, AUTOLOGIN_MAXSTR); 109 | memset(value, 0, AUTOLOGIN_MAXSTR); 110 | p = key; 111 | login_profiles[i].inuse = 1; 112 | state++; 113 | break; 114 | 115 | case ALS_PREVALUE: 116 | memset(value, 0, AUTOLOGIN_MAXSTR); 117 | p = value; 118 | state++; 119 | break; 120 | default: 121 | break; 122 | } 123 | 124 | switch (state) { 125 | case ALS_NONE: 126 | if (c == '[') { 127 | state = ALS_PREIDENTIFIER; 128 | } 129 | break; 130 | 131 | case ALS_IDENTIFIER: 132 | if (c == ']') { 133 | state = ALS_PREKEY; 134 | break; 135 | } 136 | if (c == '\n') { 137 | fprintf(stderr, _("Error on line %d in %s: New line in middle of identifier\n"), line_counter, 138 | configfile); 139 | state = ALS_NONE; 140 | break; 141 | } 142 | *p++ = c; 143 | if (p - login_profiles[i].identifier == AUTOLOGIN_MAXSTR - 1) { 144 | *p = 0; 145 | fprintf(stderr, _("Error on line %d in %s: Identifier string too long.\n"), line_counter, 146 | configfile); 147 | while ((c = fgetc(fp)) != '\n' && c != ']' && !feof(fp)) 148 | ; 149 | state = ALS_PREKEY; 150 | break; 151 | } 152 | break; 153 | 154 | case ALS_KEY: 155 | if (p == key && c == '\n') 156 | break; 157 | if (c == '=') { 158 | state = ALS_PREVALUE; 159 | break; 160 | } 161 | if (c == '[') { 162 | state = ALS_PREIDENTIFIER; 163 | break; 164 | } 165 | if (c == ' ') { /* ignore whitespace */ 166 | break; 167 | } 168 | if (c == '\n') { 169 | fprintf(stderr, _("Error on line %d in %s: Newline before '=' character\n"), line_counter, 170 | configfile); 171 | state = ALS_PREKEY; 172 | break; 173 | } 174 | *p++ = c; 175 | if (p - key == AUTOLOGIN_MAXSTR - 1) { 176 | *p = 0; 177 | fprintf(stderr, _("Error on line %d in %s: Key string too long.\n"), line_counter, configfile); 178 | while ((c = fgetc(fp)) != '\n' && c != '=' && !feof(fp)) 179 | ; 180 | if (c == '\n') { 181 | state = ALS_PREKEY; 182 | } else { 183 | state = ALS_PREVALUE; 184 | } 185 | } 186 | break; 187 | 188 | case ALS_VALUE: 189 | if (p == value && c == '\n') 190 | break; 191 | if (c == '\n') { 192 | if (strncasecmp(key, "user", AUTOLOGIN_MAXSTR) == 0) { 193 | strncpy(login_profiles[i].username, value, AUTOLOGIN_MAXSTR); 194 | login_profiles[i].hasUsername = 1; 195 | } else if (strncasecmp(key, "password", AUTOLOGIN_MAXSTR) == 0) { 196 | strncpy(login_profiles[i].password, value, AUTOLOGIN_MAXSTR); 197 | login_profiles[i].hasPassword = 1; 198 | } else { 199 | fprintf(stderr, _("Warning on line %d of %s: Unknown parameter %s, ignoring.\n"), line_counter, 200 | configfile, key); 201 | } 202 | state = ALS_PREKEY; 203 | break; 204 | } 205 | if (c == ' ') { /* ignore whitespace */ 206 | break; 207 | } 208 | *p++ = c; 209 | if (p - value == AUTOLOGIN_MAXSTR - 1) { 210 | *p = 0; 211 | fprintf(stderr, _("Error on line %d in %s: Value string too long.\n"), line_counter, configfile); 212 | while ((c = fgetc(fp)) != '\n' && !feof(fp)) 213 | ; 214 | if (c == '\n') { 215 | state = ALS_PREKEY; 216 | } 217 | } 218 | break; 219 | 220 | default: 221 | break; 222 | } 223 | if (c == '\n') { 224 | line_counter++; 225 | } 226 | if (feof(fp)) { 227 | break; 228 | } 229 | } 230 | 231 | done: 232 | fclose(fp); 233 | 234 | return 1; 235 | } 236 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | [![Build](https://github.com/haakonnessjoen/MAC-Telnet/actions/workflows/build.yaml/badge.svg?branch=master)](https://github.com/haakonnessjoen/MAC-Telnet/actions/workflows/build.yaml) 2 | [![Docker release](https://github.com/haakonnessjoen/MAC-Telnet/actions/workflows/docker.yaml/badge.svg)](https://github.com/haakonnessjoen/MAC-Telnet/actions/workflows/docker.yaml) 3 | [![License: GPL v2+](https://img.shields.io/badge/License-GPL_v2%2b-blue)](https://github.com/haakonnessjoen/MAC-Telnet/blob/master/LICENSE) 4 | 5 | # MAC-Telnet for Posix systems 6 | 7 | This project contains a set of console tools for connecting to and serving devices using the MikroTik RouterOS MAC-Telnet protocol. This is a proprietary network protocol used by MikroTik RouterOS devices to provide shell access to their devices even if the device is not configured with an ip address. 8 | 9 | The protocol is based on sending and receiving udp broadcast packets, so it is **not secure** in any means. It should only be used as a last resort for configuring a device lacking an ip address, or in a secure network environment. 10 | 11 | In addition to a client and server, this project also includes a ping tool, that can be used to ping RouterOS devices using their MAC address, and a MNDP tool, that can be used to discover RouterOS and MAC-Telnet devices on the local network. 12 | 13 | ## New EC-SRP key sharing and authentication support (RouterOS >= v6.43) 14 | 15 | The MAC-Telnet client and server now supports the new EC-SRP authentication that is mandatory after Mikrotik removed support for MD5 authentication in RouterOS v6.43 and forward. Support for using the old MD5 authentication is still possible via command line flags for backwards comatibility. 16 | 17 | ## Support for password hashing on the server side 18 | 19 | With the new EC-SRP authentication, the MAC-Telnet server now supports password hashing for the user file. This means that the server can store hashed passwords in a file, instead of plaintext passwords. To add/update users with the new hashed password support, use the `-a` flag with the `mactelnetd` command. You can also list users with the `-l` flag, or delete users from the user file with `-d`. 20 | 21 | **Note:** These commands can be used while the server is running to update the user database without restarting the server. 22 | 23 | ## Installation 24 | 25 | > [!TIP] 26 | > If you only want the `mactelnet` client tools, and not the `mactelnetd` server when compiling from source, you can add the `--without-mactelnetd` flag to the `./configure` command before compiling. 27 | 28 | ### Docker 29 | 30 | [`ghcr.io/haakonnessjoen/mac-telnet`](https://github.com/haakonnessjoen/MAC-Telnet/pkgs/container/mac-telnet) contains the latest release of all four programs: 31 | 32 | docker run -it --rm --net=host haakonn/mactelnet mactelnet … 33 | docker run -it --rm --net=host haakonn/mactelnet macping … 34 | docker run -it --rm --net=host haakonn/mactelnet mndp … 35 | docker run -it --rm --net=host haakonn/mactelnet mactelnetd … 36 | 37 | Note that Docker runs containers on isolated internal networks by default. [`--net=host`](https://docs.docker.com/network/host/) instructs Docker to provide `mactelnet` direct access to the host machine's network interfaces. 38 | 39 | See [Usage](#usage) for more. 40 | 41 | ### CentOS 7 42 | 43 | To install dependencies: 44 | 45 | yum -y install wget automake gettext gettext-devel libbsd-devel gcc make 46 | 47 | Download source tarball, extract, compile and install: 48 | 49 | wget http://github.com/haakonnessjoen/MAC-Telnet/tarball/master -O mactelnet.tar.gz 50 | tar zxvf mactelnet.tar.gz 51 | cd haakonness*/ 52 | ./autogen.sh 53 | make all install 54 | 55 | ### Linux (Debian/Ubuntu) 56 | 57 | The latest releases are usually available in the lastest versions of Debian and Ubuntu. You can install them with `apt install mactelnet-client` or `apt install mactelnet-server`. 58 | 59 | To install the lastest `master` branch *from source*, use the following instructions: 60 | 61 | apt-get install build-essential autopoint automake autoconf libbsd-dev libssl-dev gettext 62 | 63 | Download source tarball, extract, compile and install: 64 | 65 | wget http://github.com/haakonnessjoen/MAC-Telnet/tarball/master -O mactelnet.tar.gz 66 | tar zxvf mactelnet.tar.gz 67 | cd haakonness*/ 68 | ./autogen.sh 69 | make all install 70 | 71 | ### FreeBSD 72 | 73 | Dependencies: clang (gcc or similar), automake, autoconf 74 | 75 | To install dependencies on FreeBSD: 76 | 77 | pkg install automake autoconf gettext-tools 78 | 79 | Download source tarball, extract, compile and install: 80 | 81 | wget http://github.com/haakonnessjoen/MAC-Telnet/tarball/master -O mactelnet.tar.gz 82 | tar zxvf mactelnet.tar.gz 83 | cd haakonness*/ 84 | ./autogen.sh 85 | ./configure LDFLAGS=" -L/usr/local/lib" 86 | gmake all install 87 | 88 | ### Mac OS X 89 | 90 | Download source tarball and extract: 91 | 92 | wget http://github.com/haakonnessjoen/MAC-Telnet/tarball/master -O mactelnet.tar.gz 93 | tar zxvf mactelnet.tar.gz 94 | cd haakonness*/ 95 | 96 | Install dependencies 97 | 98 | brew install gettext autoconf automake libtool openssl pkg-config 99 | 100 | Set up build environment, compile and install: 101 | 102 | export GETTEXT_PATH=$(brew --prefix gettext) 103 | export OPENSSL_PATH=$(brew --prefix openssl) 104 | export PATH="${GETTEXT_PATH}/bin:${OPENSSL_PATH}/bin:$PATH" 105 | export LDFLAGS="-L${GETTEXT_PATH}/lib" 106 | export CPPFLAGS="-I${GETTEXT_PATH}/include -I${OPENSSL_PATH}/include" 107 | export CRYPTO_CFLAGS="-I${OPENSSL_PATH}/include" 108 | export CRYPTO_LIBS="-L${OPENSSL_PATH}/lib ${OPENSSL_PATH}/lib/libcrypto.3.dylib" 109 | ./autogen.sh 110 | make all install 111 | 112 | ## Usage 113 | 114 | # mactelnet -h 115 | Usage: mactelnet [-h] [-n] [-a ] [-A] [-t ] [-u ] [-p ] [-U ] | -l [-B] [-t ] 116 | 117 | Parameters: 118 | MAC MAC-Address of the RouterOS/mactelnetd device. Use mndp to 119 | discover it. 120 | identity The identity/name of your destination device. Uses 121 | MNDP protocol to find it. 122 | -l List/Search for routers nearby (MNDP). You may use -t to set timeout. 123 | -B Batch mode. Use computer readable output (CSV), for use with -l. 124 | -n Do not use broadcast packets. Less insecure but requires 125 | root privileges. 126 | -a Use specified path instead of the default: ~/.mactelnet for autologin config file. 127 | -A Disable autologin feature. 128 | -t Amount of seconds to wait for a response on each interface. 129 | -u Specify username on command line. 130 | -p Specify password on command line. 131 | -U Drop privileges to this user. Used in conjunction with -n 132 | for security. 133 | -q Quiet mode. 134 | -o Force old MD5 authentication method. 135 | -h This help. 136 | 137 | Example using identity: 138 | 139 | $ mactelnet main-router 140 | Searching for 'main-router'...found 141 | Login: admin 142 | Password: 143 | Connecting to d4:ca:6d:12:47:13...done 144 | 145 | Example using mac address: 146 | 147 | $ mactelnet 0:c:42:43:58:a5 148 | Login: admin 149 | Password: 150 | Connecting to 0:c:42:43:58:a5...done 151 | 152 | 153 | MMM MMM KKK TTTTTTTTTTT KKK 154 | MMMM MMMM KKK TTTTTTTTTTT KKK 155 | MMM MMMM MMM III KKK KKK RRRRRR OOOOOO TTT III KKK KKK 156 | MMM MM MMM III KKKKK RRR RRR OOO OOO TTT III KKKKK 157 | MMM MMM III KKK KKK RRRRRR OOO OOO TTT III KKK KKK 158 | MMM MMM III KKK KKK RRR RRR OOOOOO TTT III KKK KKK 159 | 160 | MikroTik RouterOS 6.49 (c) 1999-2021 http://www.mikrotik.com/ 161 | 162 | 163 | [admin@Mikrotik] > 164 | 165 | ### Tips 166 | 167 | You can use the well known "expect" tool to automate/script dialogues via mactelnet! 168 | 169 | ### List available hosts using MNDP Discovery 170 | 171 | # mactelnet -l 172 | 173 | ## MAC-Ping usage 174 | 175 | # macping -h 176 | Usage: macping [-h] [-c ] [-s ] 177 | 178 | Parameters: 179 | MAC MAC-Address of the RouterOS/mactelnetd device. 180 | -s Specify size of ping packet. 181 | -c Number of packets to send. (0 = for ever) 182 | -h This help. 183 | 184 | Example: 185 | 186 | # macping 0:c:42:43:58:a5 187 | 0:c:42:43:58:a5 56 byte, ping time 1.17 ms 188 | 0:c:42:43:58:a5 56 byte, ping time 1.07 ms 189 | 0:c:42:43:58:a5 56 byte, ping time 1.20 ms 190 | 0:c:42:43:58:a5 56 byte, ping time 0.65 ms 191 | 0:c:42:43:58:a5 56 byte, ping time 1.19 ms 192 | 193 | 5 packets transmitted, 5 packets received, 0% packet loss 194 | round-trip min/avg/max = 0.65/1.06/1.20 ms 195 | 196 | Or for use in bash-scripting: 197 | 198 | # macping 0:c:42:43:58:a5 -c 2 >/dev/null 2>&1 || ( echo "No answer for 2 pings" | mail -s "router down" my.email@address.com ) 199 | 200 | ## Huge thanks 201 | 202 | - Thanks to [@comed-ian](https://github.com/comed-ian) for creating a working proof of concept python script that successfully authenticated using the new authentication method in RouterOS 4.43+, and [@kmeaw](https://github.com/kmeaw) for porting the code to C, and implementing it in mactelnet and mactelnetd. 203 | - Thanks to Omni Flux for doing [the initial reverse engineering](https://omniflux.com/devel/mikrotik/Mikrotik_MAC_Telnet_Procotol.txt) of the MAC Telnet protocol, that inspired me to write these programs, as well as the mactelnet Wireshark plugin. 204 | -------------------------------------------------------------------------------- /src/macping.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #if defined(__FreeBSD__) || defined(__APPLE__) 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define ETH_FRAME_LEN ETHER_MAX_LEN 31 | #define ETH_ALEN ETHER_ADDR_LEN 32 | #else 33 | #include 34 | #endif 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "config.h" 43 | #include "protocol.h" 44 | #include "interfaces.h" 45 | #include "utlist.h" 46 | #include "extra.h" 47 | 48 | #define MAX_DEVICES 128 49 | #define MT_INTERFACE_LEN 128 50 | 51 | #define PROGRAM_NAME "MAC-Ping" 52 | 53 | #define _(STRING) gettext(STRING) 54 | 55 | static int sockfd, insockfd; 56 | 57 | static unsigned short ping_size = 38; 58 | 59 | struct net_interface *interfaces; 60 | 61 | static struct in_addr sourceip; 62 | static struct in_addr destip; 63 | static unsigned char dstmac[ETH_ALEN]; 64 | 65 | static int ping_sent = 0; 66 | static int pong_received = 0; 67 | static float min_ms = FLT_MAX; 68 | static float avg_ms = 0; 69 | static float max_ms = 0; 70 | 71 | /* Protocol data direction, not used here, but obligatory for protocol.c */ 72 | unsigned char mt_direction_fromserver = 0; 73 | 74 | static void print_version() { 75 | fprintf(stderr, PROGRAM_NAME " " PACKAGE_VERSION "\n"); 76 | } 77 | 78 | static long long int toddiff(struct timeval *tod1, struct timeval *tod2) { 79 | long long t1, t2; 80 | t1 = tod1->tv_sec * 1000000 + tod1->tv_usec; 81 | t2 = tod2->tv_sec * 1000000 + tod2->tv_usec; 82 | return t1 - t2; 83 | } 84 | 85 | static void display_results() { 86 | int percent = (int)((100.f / ping_sent) * pong_received); 87 | if (percent > 100) { 88 | percent = 0; 89 | } 90 | 91 | if (percent < 0) { 92 | percent = 0; 93 | } 94 | 95 | if (min_ms == FLT_MAX) { 96 | min_ms = 0; 97 | } 98 | 99 | printf("\n"); 100 | printf(_("%d packets transmitted, %d packets received, %d%% packet loss\n"), ping_sent, pong_received, 101 | 100 - percent); 102 | printf(_("round-trip min/avg/max = %.2f/%.2f/%.2f ms\n"), min_ms, avg_ms / pong_received, max_ms); 103 | 104 | /* For bash scripting */ 105 | if (pong_received == 0) { 106 | exit(1); 107 | } 108 | 109 | exit(0); 110 | } 111 | 112 | int main(int argc, char **argv) { 113 | int print_help = 0; 114 | int send_packets = 5; 115 | int fastmode = 0; 116 | int c; 117 | struct sockaddr_in si_me; 118 | struct mt_packet packet; 119 | int i; 120 | 121 | setlocale(LC_ALL, ""); 122 | bindtextdomain(PACKAGE, LOCALEDIR); 123 | textdomain(PACKAGE); 124 | 125 | while (1) { 126 | c = getopt(argc, argv, "fs:c:hv?"); 127 | 128 | if (c == -1) { 129 | break; 130 | } 131 | 132 | switch (c) { 133 | case 'f': 134 | fastmode = 1; 135 | break; 136 | 137 | case 's': 138 | ping_size = atoi(optarg) - 18; 139 | break; 140 | 141 | case 'v': 142 | print_version(); 143 | exit(0); 144 | break; 145 | 146 | case 'c': 147 | send_packets = atoi(optarg); 148 | break; 149 | 150 | case 'h': 151 | case '?': 152 | print_help = 1; 153 | break; 154 | } 155 | } 156 | 157 | /* We don't want people to use this for the wrong reasons */ 158 | if (fastmode && (send_packets <= 0 || send_packets > 100)) { 159 | fprintf(stderr, _("Number of packets to send must be more than 0 and up to 100 in fast mode.\n")); 160 | return 1; 161 | } 162 | 163 | if (argc - optind < 1 || print_help) { 164 | print_version(); 165 | fprintf(stderr, _("Usage: %s [-h] [-f] [-c ] [-s ]\n"), argv[0]); 166 | 167 | if (print_help) { 168 | fprintf(stderr, _("\nParameters:\n" 169 | " MAC MAC-Address of the RouterOS/mactelnetd device.\n" 170 | " -f Fast mode, do not wait before sending next ping request.\n" 171 | " -s Specify size of ping packet.\n" 172 | " -c Number of packets to send. (0 = unlimited)\n" 173 | " -h This help.\n" 174 | "\n")); 175 | } 176 | return 1; 177 | } 178 | 179 | if (ping_size > ETH_FRAME_LEN - 42) { 180 | fprintf(stderr, _("Packet size must be between 18 and %d\n"), ETH_FRAME_LEN - 42 + 18); 181 | exit(1); 182 | } 183 | 184 | /* Mikrotik RouterOS does not answer unless the packet has the correct recipient mac-address in 185 | * the ethernet frame. Unlike real MacTelnet connections where the OS is ok with it being a 186 | * broadcast mac address. 187 | */ 188 | if (geteuid() != 0) { 189 | fprintf(stderr, _("You need to have root privileges to use %s.\n"), argv[0]); 190 | return 1; 191 | } 192 | 193 | /* Get mac-address from string, or check for hostname via mndp */ 194 | if (!query_mndp_or_mac(argv[optind], dstmac, 1)) { 195 | /* No valid mac address found, abort */ 196 | return 1; 197 | } 198 | 199 | sockfd = net_init_raw_socket(); 200 | 201 | insockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 202 | if (insockfd < 0) { 203 | perror("insockfd"); 204 | return 1; 205 | } 206 | 207 | /* Set initialize address/port */ 208 | memset((char *)&si_me, 0, sizeof(si_me)); 209 | si_me.sin_family = AF_INET; 210 | si_me.sin_port = htons(MT_MACTELNET_PORT); 211 | si_me.sin_addr.s_addr = htonl(INADDR_ANY); 212 | 213 | int optval = 1; 214 | setsockopt(insockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 215 | 216 | /* Bind to specified address/port */ 217 | if (bind(insockfd, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) { 218 | fprintf(stderr, _("Error binding to %s:%d\n"), inet_ntoa(si_me.sin_addr), MT_MNDP_PORT); 219 | return 1; 220 | } 221 | 222 | /* Listen address*/ 223 | inet_pton(AF_INET, (char *)"0.0.0.0", &sourceip); 224 | 225 | /* Set up global info about the connection */ 226 | inet_pton(AF_INET, (char *)"255.255.255.255", &destip); 227 | 228 | srand(time(NULL)); 229 | 230 | /* Enumerate available interfaces */ 231 | net_get_interfaces(&interfaces); 232 | 233 | if (ping_size < sizeof(struct timeval)) { 234 | ping_size = sizeof(struct timeval); 235 | } 236 | 237 | signal(SIGINT, display_results); 238 | 239 | for (i = 0; i < send_packets || send_packets <= 0; ++i) { 240 | fd_set read_fds; 241 | static struct timeval lasttimestamp; 242 | int reads, result; 243 | struct timeval timeout; 244 | int ii; 245 | int sent = 0; 246 | int waitforpacket; 247 | struct timeval timestamp; 248 | unsigned char pingdata[MT_PACKET_LEN]; 249 | struct net_interface *interface; 250 | 251 | gettimeofday(×tamp, NULL); 252 | memcpy(pingdata, ×tamp, sizeof(timestamp)); 253 | for (ii = sizeof(timestamp); ii < ping_size; ++ii) { 254 | pingdata[ii] = rand() % 256; 255 | } 256 | 257 | LL_FOREACH(interfaces, interface) { 258 | if (!interface->has_mac) { 259 | continue; 260 | } 261 | 262 | init_pingpacket(&packet, interface->mac_addr, dstmac); 263 | add_packetdata(&packet, pingdata, ping_size); 264 | result = net_send_udp(sockfd, interface, interface->mac_addr, dstmac, &sourceip, MT_MACTELNET_PORT, &destip, 265 | MT_MACTELNET_PORT, packet.data, packet.size); 266 | 267 | if (result > 0) { 268 | sent++; 269 | } 270 | } 271 | if (sent == 0) { 272 | fprintf(stderr, _("Error sending packet.\n")); 273 | continue; 274 | } 275 | ping_sent++; 276 | 277 | FD_ZERO(&read_fds); 278 | FD_SET(insockfd, &read_fds); 279 | 280 | timeout.tv_sec = 1; 281 | timeout.tv_usec = 0; 282 | 283 | waitforpacket = 1; 284 | 285 | while (waitforpacket) { 286 | /* Wait for data or timeout */ 287 | reads = select(insockfd + 1, &read_fds, NULL, NULL, &timeout); 288 | if (reads <= 0) { 289 | waitforpacket = 0; 290 | fprintf(stderr, _("%s ping timeout\n"), ether_ntoa((struct ether_addr *)&dstmac)); 291 | break; 292 | } 293 | 294 | unsigned char buff[MT_PACKET_LEN]; 295 | struct sockaddr_in saddress; 296 | unsigned int slen = sizeof(saddress); 297 | struct mt_mactelnet_hdr pkthdr; 298 | 299 | result = recvfrom(insockfd, buff, sizeof(buff), 0, (struct sockaddr *)&saddress, &slen); 300 | /* Check for exact size */ 301 | if (result != 18 + ping_size) { 302 | continue; 303 | } 304 | parse_packet(buff, &pkthdr); 305 | 306 | /* TODO: Check that we are the receiving host */ 307 | if (pkthdr.ptype != MT_PTYPE_PONG) { 308 | /* Wait for the correct packet */ 309 | continue; 310 | } 311 | 312 | struct timeval pongtimestamp; 313 | struct timeval nowtimestamp; 314 | 315 | waitforpacket = 0; 316 | gettimeofday(&nowtimestamp, NULL); 317 | 318 | memcpy(&pongtimestamp, pkthdr.data - 4, sizeof(pongtimestamp)); 319 | if (memcmp(pkthdr.data - 4, pingdata, ping_size) == 0) { 320 | float diff = toddiff(&nowtimestamp, &pongtimestamp) / 1000.0f; 321 | 322 | if (diff < min_ms) { 323 | min_ms = diff; 324 | } 325 | 326 | if (diff > max_ms) { 327 | max_ms = diff; 328 | } 329 | 330 | avg_ms += diff; 331 | 332 | printf(_("%s %d byte, ping time %.2f ms%s\n"), ether_ntoa((struct ether_addr *)&(pkthdr.srcaddr)), 333 | result, diff, 334 | (char *)(memcmp(&pongtimestamp, &lasttimestamp, sizeof(lasttimestamp)) == 0 ? " DUP" : "")); 335 | } else { 336 | printf(_("%s Reply of %d bytes of unequal data\n"), ether_ntoa((struct ether_addr *)&(pkthdr.srcaddr)), 337 | result); 338 | } 339 | pong_received++; 340 | memcpy(&lasttimestamp, &pongtimestamp, sizeof(pongtimestamp)); 341 | if (!fastmode) { 342 | sleep(1); 343 | } 344 | } 345 | } 346 | 347 | /* Display statistics and exit */ 348 | display_results(); 349 | 350 | return 0; 351 | } 352 | -------------------------------------------------------------------------------- /src/users.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include "config.h" 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "mtwei.h" 33 | #include "extra.h" 34 | #include "users.h" 35 | #include "utlist.h" 36 | 37 | #define _(STRING) gettext(STRING) 38 | 39 | struct mt_credentials *mt_users = NULL; 40 | 41 | static int parseLine(char *line, char **username, char **password, char **salt) { 42 | char *user; 43 | char *pass; 44 | char *sal; 45 | 46 | user = strtok(line, ":"); 47 | int userlen = strlen(user); 48 | 49 | if (strstr(user+userlen+1, ":") != NULL) { 50 | pass = strtok(NULL, ":"); 51 | sal = strtok(NULL, "\n"); 52 | } else { 53 | pass = strtok(NULL, "\n"); 54 | sal = NULL; 55 | } 56 | 57 | if (user == NULL || pass == NULL || user[0] == '#') { 58 | return 0; 59 | } 60 | 61 | if (userlen > MT_CRED_USERLEN) { 62 | userlen = MT_CRED_USERLEN; 63 | } 64 | user[userlen] = '\0'; 65 | if (strlen(pass) > MT_CRED_LEN) { 66 | pass[MT_CRED_LEN] = '\0'; 67 | } 68 | if (sal != NULL && strlen(sal) > MT_CRED_SALTLEN * 2) { 69 | sal[MT_CRED_SALTLEN * 2] = '\0'; 70 | } 71 | if (sal != NULL && strlen(pass) > MT_CRED_HASHLEN * 2) { 72 | pass[MT_CRED_HASHLEN * 2] = '\0'; 73 | } 74 | 75 | *username = user; 76 | *password = pass; 77 | *salt = sal; 78 | 79 | return 1; 80 | } 81 | 82 | // Returns 1 if the file is ok, 0 if not 83 | static int check_user_file(struct stat *info) { 84 | if (stat(USERSFILE, info) != 0) { 85 | fprintf(stderr, _("Error stating file %s: %s\n"), USERSFILE, strerror(errno)); 86 | return 0; 87 | } 88 | 89 | struct passwd *pwd = getpwuid(info->st_uid); 90 | if (pwd == NULL) { 91 | fprintf(stderr, _("Error getting user information for uid %d: %s\n"), info->st_uid, strerror(errno)); 92 | return 0; 93 | } 94 | 95 | if (strcmp(pwd->pw_name, "root") != 0) { 96 | fprintf(stderr, _("Error: %s is not owned by root\n"), USERSFILE); 97 | return 0; 98 | } 99 | 100 | if (info->st_mode & S_IWOTH || info->st_mode & S_IWGRP) { 101 | fprintf(stderr, 102 | _("Error: %s is writable by others, It should have permissions set to 0600 for better security\n"), 103 | USERSFILE); 104 | return 0; 105 | } 106 | 107 | return 1; 108 | } 109 | 110 | void read_userfile() { 111 | int lineno = 0; 112 | struct mt_credentials *cred, *tmp; 113 | 114 | struct stat info; 115 | int file_ok = check_user_file(&info); 116 | if (!file_ok) { 117 | int counter = 0; 118 | DL_COUNT(mt_users, cred, counter); 119 | if (counter == 0) { 120 | // If the file is not usable, and we have no users, we should abort 121 | fprintf(stderr, _("Error: %s is invalid and no users known, aborting.\n"), USERSFILE); 122 | exit(EXIT_FAILURE); 123 | } 124 | // If the file is not owned by root, or if it is writable by others, but we have read the users file before, we can continue 125 | // without the updated users file. 126 | fprintf(stderr, _("Warning: User file '%s' is not readable, falling back to known users.\n"), USERSFILE); 127 | return; 128 | } 129 | 130 | FILE *file = fopen(USERSFILE, "r"); 131 | char line[BUFSIZ]; 132 | 133 | if (file == NULL) { 134 | perror(USERSFILE); 135 | exit(1); 136 | } 137 | 138 | DL_FOREACH_SAFE(mt_users, cred, tmp) { 139 | DL_DELETE(mt_users, cred); 140 | free(cred); 141 | } 142 | 143 | while (fgets(line, sizeof line, file)) { 144 | char *user; 145 | char *password; 146 | char *salt; 147 | size_t size; 148 | 149 | lineno++; 150 | 151 | if (!parseLine(line, &user, &password, &salt)) { 152 | continue; 153 | } 154 | 155 | cred = (struct mt_credentials *)calloc(1, sizeof(struct mt_credentials)); 156 | if (cred == NULL) { 157 | fprintf(stderr, _("Error allocating memory for user information\n")); 158 | exit(1); 159 | } 160 | 161 | /* verify that the username & password will be '\0' terminated */ 162 | memcpy(cred->username, user, size = (strlen(user) < MT_CRED_LEN ? strlen(user) : MT_CRED_LEN - 1)); 163 | cred->username[size] = '\0'; 164 | if (salt != NULL) { 165 | if (strlen(password) != MT_CRED_HASHLEN * 2) { 166 | fprintf(stderr, _("Warning: Invalid password hash on line %d of user file\n"), lineno); 167 | free(cred); 168 | continue; 169 | } 170 | if (strlen(salt) != MT_CRED_SALTLEN * 2) { 171 | fprintf(stderr, _("Warning: Invalid salt on line %d of user file\n"), lineno); 172 | free(cred); 173 | continue; 174 | } 175 | long readlen; 176 | unsigned char *binsalt; 177 | if ((binsalt = OPENSSL_hexstr2buf(salt, &readlen)) == NULL || readlen != MT_CRED_SALTLEN) { 178 | fprintf(stderr, _("Warning: Invalid salt on line %d of user file\n"), lineno); 179 | free(cred); 180 | continue; 181 | } 182 | memcpy(cred->salt, binsalt, MT_CRED_SALTLEN); 183 | 184 | readlen = 0; 185 | unsigned char *binpass; 186 | if ((binpass = OPENSSL_hexstr2buf(password, &readlen)) == NULL || readlen != MT_CRED_HASHLEN) { 187 | fprintf(stderr, _("Warning: Invalid password hash on line %d of user file\n"), lineno); 188 | free(cred); 189 | continue; 190 | } 191 | memcpy(cred->password, binpass, MT_CRED_HASHLEN); 192 | cred->hashed = 1; 193 | } else { 194 | memcpy(cred->password, password, 195 | size = (strlen(password) < MT_CRED_LEN ? strlen(password) : MT_CRED_LEN - 1)); 196 | cred->password[size] = '\0'; 197 | } 198 | DL_APPEND(mt_users, cred); 199 | } 200 | fclose(file); 201 | } 202 | 203 | struct mt_credentials *find_user(char *username) { 204 | struct mt_credentials *cred; 205 | 206 | DL_FOREACH(mt_users, cred) { 207 | if (strcmp(username, cred->username) == 0) { 208 | return cred; 209 | } 210 | } 211 | return NULL; 212 | } 213 | 214 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 215 | /* 216 | * Filter out colons from the decoded string. 217 | * By default, the OPENSSL_buf2hexstr function in OpenSSL 1.1 218 | * uses colons as a byte separator, and this cannot be overridden. 219 | */ 220 | static void remove_colons(char *s) { 221 | const char *p = s; 222 | char *q = s; 223 | while (*p != '\0') { 224 | *q = *p++; 225 | q += (*q != ':'); 226 | } 227 | 228 | *q = '\0'; 229 | } 230 | #endif 231 | 232 | int add_user(const char *username, const char *password) { 233 | FILE *rfile; 234 | FILE *wfile; 235 | char line[BUFSIZ]; 236 | char linecopy[BUFSIZ]; 237 | unsigned char newsalt[MT_CRED_SALTLEN]; 238 | unsigned char newhash[MT_CRED_HASHLEN]; 239 | unsigned int md_len; 240 | char found = 0; 241 | int lineno = 0; 242 | 243 | // Check that the file USERSFILE is owned by root with stat(), and that it is not writable by others 244 | // If not, exit with failure 245 | struct stat info; 246 | int is_ok = check_user_file(&info); 247 | if (!is_ok) { 248 | exit(EXIT_FAILURE); 249 | } 250 | 251 | // Open the password file 252 | rfile = fopen(USERSFILE, "r"); 253 | if (!rfile) { 254 | fprintf(stderr, _("Error opening password file %s: %s\n"), USERSFILE, strerror(errno)); 255 | exit(EXIT_FAILURE); 256 | } 257 | wfile = fopen(USERSFILE ".tmp", "wb"); 258 | if (!wfile) { 259 | fprintf(stderr, _("Error opening temporary password file for writing %s: %s\n"), USERSFILE ".tmp", 260 | strerror(errno)); 261 | exit(EXIT_FAILURE); 262 | } 263 | 264 | if (fchown(fileno(wfile), info.st_uid, info.st_gid) != 0) { 265 | fprintf(stderr, _("Error changing ownership of temporary password file %s: %s\n"), USERSFILE ".tmp", 266 | strerror(errno)); 267 | fclose(wfile); 268 | unlink(USERSFILE ".tmp"); 269 | exit(EXIT_FAILURE); 270 | } 271 | 272 | if (fchmod(fileno(wfile), info.st_mode) != 0) { 273 | fprintf(stderr, _("Error changing permissions of temporary password file %s: %s\n"), USERSFILE ".tmp", 274 | strerror(errno)); 275 | fclose(wfile); 276 | unlink(USERSFILE ".tmp"); 277 | exit(EXIT_FAILURE); 278 | } 279 | 280 | // Generate a random salt 281 | if (!RAND_bytes(newsalt, sizeof(newsalt))) { 282 | fprintf(stderr, _("Error generating random salt.\n")); 283 | exit(EXIT_FAILURE); 284 | } 285 | 286 | if (password != NULL) { 287 | mtwei_id(username, password, newsalt, newhash); 288 | } 289 | 290 | while (fgets(line, sizeof line, rfile)) { 291 | char *user; 292 | char *pass; 293 | char *sal; 294 | 295 | lineno++; 296 | 297 | memcpy(linecopy, line, sizeof linecopy); 298 | if (!parseLine(linecopy, &user, &pass, &sal)) { 299 | fputs(line, wfile); 300 | continue; 301 | } 302 | 303 | if (!found && strcmp(user, username) == 0) { 304 | if (password == NULL) { 305 | // Delete the user 306 | found = 1; 307 | continue; 308 | } 309 | fprintf(wfile, "%s:", username); 310 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 311 | char *output; 312 | output = OPENSSL_buf2hexstr(newhash, MT_CRED_HASHLEN); 313 | remove_colons(output); 314 | #else 315 | char output[MT_CRED_HASHLEN * 2 + 1]; 316 | OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newhash, MT_CRED_HASHLEN, '\0'); 317 | #endif 318 | fputs(output, wfile); 319 | fputs(":", wfile); 320 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 321 | OPENSSL_free(output); 322 | output = OPENSSL_buf2hexstr(newsalt, MT_CRED_SALTLEN); 323 | remove_colons(output); 324 | #else 325 | OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newsalt, MT_CRED_SALTLEN, '\0'); 326 | #endif 327 | fputs(output, wfile); 328 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 329 | OPENSSL_free(output); 330 | #endif 331 | fputs("\n", wfile); 332 | found = 1; 333 | } else { 334 | fputs(line, wfile); 335 | } 336 | } 337 | 338 | // Non-existing user, append to the end of the file 339 | if (!found && password != NULL) { 340 | // Write username, salt, and hashed password to the file 341 | fprintf(wfile, "%s:", username); 342 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 343 | char *output; 344 | output = OPENSSL_buf2hexstr(newhash, MT_CRED_HASHLEN); 345 | remove_colons(output); 346 | #else 347 | char output[MT_CRED_HASHLEN * 2 + 1]; 348 | OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newhash, MT_CRED_HASHLEN, '\0'); 349 | #endif 350 | fputs(output, wfile); 351 | fputs(":", wfile); 352 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 353 | OPENSSL_free(output); 354 | output = OPENSSL_buf2hexstr(newsalt, MT_CRED_SALTLEN); 355 | remove_colons(output); 356 | #else 357 | OPENSSL_buf2hexstr_ex(output, sizeof(output), NULL, newsalt, MT_CRED_SALTLEN, '\0'); 358 | #endif 359 | fputs(output, wfile); 360 | #if OPENSSL_VERSION_NUMBER < 0x030000000 // less than 3.0.0 361 | OPENSSL_free(output); 362 | #endif 363 | fputs("\n", wfile); 364 | } 365 | 366 | // Close the password file 367 | fclose(wfile); 368 | fclose(rfile); 369 | 370 | // Rename the temporary file to the password file 371 | if (rename(USERSFILE ".tmp", USERSFILE) != 0) { 372 | fprintf(stderr, "Error renaming temporary password file to %s: %s\n", USERSFILE, strerror(errno)); 373 | unlink(USERSFILE ".tmp"); 374 | exit(EXIT_FAILURE); 375 | } 376 | 377 | return found ? 2 : 1; 378 | } 379 | -------------------------------------------------------------------------------- /src/protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #define _BSD_SOURCE 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #ifdef __LINUX__ 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #if defined(__FreeBSD__) || defined(__APPLE__) 32 | #include 33 | #include 34 | #include 35 | #else 36 | #include 37 | #endif 38 | #include 39 | #if defined(__APPLE__) 40 | #include 41 | #define le32toh OSSwapLittleToHostInt32 42 | #elif defined(__FreeBSD__) 43 | #include 44 | #else 45 | #include 46 | #endif 47 | 48 | #include "config.h" 49 | #include "protocol.h" 50 | #include "extra.h" 51 | 52 | #define _(STRING) gettext(STRING) 53 | 54 | int init_packet(struct mt_packet *packet, enum mt_ptype ptype, unsigned char *srcmac, unsigned char *dstmac, 55 | unsigned short sessionkey, unsigned int counter) { 56 | unsigned char *data = packet->data; 57 | 58 | /* Packet version */ 59 | data[0] = 1; 60 | 61 | /* Packet type */ 62 | data[1] = ptype; 63 | 64 | /* src ethernet address */ 65 | memcpy(data + 2, srcmac, ETH_ALEN); 66 | 67 | /* dst ethernet address */ 68 | memcpy(data + 8, dstmac, ETH_ALEN); 69 | 70 | /* Session key */ 71 | sessionkey = htons(sessionkey); 72 | memcpy(data + (mt_direction_fromserver ? 16 : 14), &sessionkey, sizeof(sessionkey)); 73 | 74 | /* Client type: Mac Telnet */ 75 | memcpy(data + (mt_direction_fromserver ? 14 : 16), &mt_mactelnet_clienttype, sizeof(mt_mactelnet_clienttype)); 76 | 77 | /* Received/sent data counter */ 78 | counter = htonl(counter); 79 | memcpy(data + 18, &counter, sizeof(counter)); 80 | 81 | /* 22 bytes header */ 82 | packet->size = 22; 83 | return 22; 84 | } 85 | 86 | int add_control_packet(struct mt_packet *packet, enum mt_cptype cptype, void *cpdata, unsigned short data_len) { 87 | unsigned char *data = packet->data + packet->size; 88 | unsigned int act_size = data_len + (cptype == MT_CPTYPE_PLAINDATA ? 0 : MT_CPHEADER_LEN); 89 | 90 | /* Something is really wrong. Packets should never become over 1500 bytes, 91 | perform an Integer-Overflow safe check */ 92 | if (act_size > MT_PACKET_LEN - packet->size) { 93 | fprintf(stderr, _("add_control_packet: ERROR, too large packet. Exceeds %d bytes\n"), MT_PACKET_LEN); 94 | return -1; 95 | // exit(1); 96 | } 97 | 98 | /* PLAINDATA isn't really a controlpacket, but we handle it here, since 99 | parseControlPacket also parses raw data as PLAINDATA */ 100 | if (cptype == MT_CPTYPE_PLAINDATA) { 101 | memcpy(data, cpdata, data_len); 102 | packet->size += data_len; 103 | return data_len; 104 | } 105 | 106 | /* Control Packet Magic id */ 107 | memcpy(data, mt_mactelnet_cpmagic, sizeof(mt_mactelnet_cpmagic)); 108 | 109 | /* Control packet type */ 110 | data[4] = cptype; 111 | 112 | /* Data length */ 113 | #if BYTE_ORDER == LITTLE_ENDIAN 114 | { 115 | unsigned int templen; 116 | templen = htonl(data_len); 117 | memcpy(data + 5, &templen, sizeof(templen)); 118 | } 119 | #else 120 | memcpy(data + 5, &data_len, sizeof(data_len)); 121 | #endif 122 | 123 | /* Insert data */ 124 | if (data_len > 0) { 125 | memcpy(data + MT_CPHEADER_LEN, cpdata, data_len); 126 | } 127 | 128 | packet->size += act_size; 129 | /* Control packet header length + data length */ 130 | return act_size; 131 | } 132 | 133 | int init_pingpacket(struct mt_packet *packet, unsigned char *srcmac, unsigned char *dstmac) { 134 | init_packet(packet, MT_PTYPE_PING, srcmac, dstmac, 0, 0); 135 | 136 | /* Zero out sessionkey & counter */ 137 | bzero(packet->data + 14, 4); 138 | 139 | /* Remove data counter field from header */ 140 | packet->size -= 4; 141 | return packet->size; 142 | } 143 | 144 | int init_pongpacket(struct mt_packet *packet, unsigned char *srcmac, unsigned char *dstmac) { 145 | init_packet(packet, MT_PTYPE_PONG, srcmac, dstmac, 0, 0); 146 | 147 | /* Zero out sessionkey & counter */ 148 | bzero(packet->data + 14, 4); 149 | 150 | /* Remove data counter field from header */ 151 | packet->size -= 4; 152 | return packet->size; 153 | } 154 | 155 | int add_packetdata(struct mt_packet *packet, unsigned char *data, unsigned short length) { 156 | /* Integer-Overflow safe check */ 157 | if (length > MT_PACKET_LEN - packet->size) { 158 | fprintf(stderr, _("add_control_packet: ERROR, too large packet. Exceeds %d bytes\n"), MT_PACKET_LEN); 159 | return -1; 160 | } 161 | 162 | memcpy(packet->data + packet->size, data, length); 163 | packet->size += length; 164 | 165 | return length; 166 | } 167 | 168 | void parse_packet(unsigned char *data, struct mt_mactelnet_hdr *pkthdr) { 169 | /* Packet version */ 170 | pkthdr->ver = data[0]; 171 | 172 | /* Packet type */ 173 | pkthdr->ptype = data[1]; 174 | 175 | /* src ethernet addr */ 176 | memcpy(pkthdr->srcaddr, data + 2, ETH_ALEN); 177 | 178 | /* dst ethernet addr */ 179 | memcpy(pkthdr->dstaddr, data + 8, ETH_ALEN); 180 | 181 | /* Session key */ 182 | memcpy(&(pkthdr->seskey), data + (mt_direction_fromserver ? 14 : 16), sizeof(pkthdr->seskey)); 183 | pkthdr->seskey = ntohs(pkthdr->seskey); 184 | 185 | /* server type */ 186 | memcpy(&(pkthdr->clienttype), data + (mt_direction_fromserver ? 16 : 14), 2); 187 | 188 | /* Received/sent data counter */ 189 | memcpy(&(pkthdr->counter), data + 18, sizeof(pkthdr->counter)); 190 | pkthdr->counter = ntohl(pkthdr->counter); 191 | 192 | /* Set pointer to actual data */ 193 | pkthdr->data = data + 22; 194 | } 195 | 196 | int parse_control_packet(unsigned char *packetdata, unsigned short data_len, struct mt_mactelnet_control_hdr *cpkthdr) { 197 | static unsigned char *int_data; 198 | static unsigned int int_data_len; 199 | static unsigned int int_pos; 200 | unsigned char *data; 201 | 202 | /* Store info so we can call this function once with data, 203 | and then several times for each control packets. Letting this function 204 | control the data position. */ 205 | if (packetdata != NULL) { 206 | if (data_len == 0) { 207 | return 0; 208 | } 209 | 210 | int_data = packetdata; 211 | int_data_len = data_len; 212 | int_pos = 0; 213 | } 214 | 215 | /* No more data to parse? */ 216 | if (int_pos >= int_data_len) { 217 | return 0; 218 | } 219 | 220 | /* Set current position in data buffer */ 221 | data = int_data + int_pos; 222 | 223 | /* Check for valid minimum packet length & magic header */ 224 | if ((int_data_len - int_pos) >= MT_CPHEADER_LEN && memcmp(data, &mt_mactelnet_cpmagic, 4) == 0) { 225 | /* Control packet type */ 226 | cpkthdr->cptype = data[4]; 227 | 228 | /* Control packet data length */ 229 | memcpy(&(cpkthdr->length), data + 5, sizeof(cpkthdr->length)); 230 | cpkthdr->length = ntohl(cpkthdr->length); 231 | 232 | /* We want no buffer overflows */ 233 | if (cpkthdr->length > int_data_len - MT_CPHEADER_LEN - int_pos) { 234 | cpkthdr->length = int_data_len - MT_CPHEADER_LEN - int_pos; 235 | } 236 | 237 | /* Set pointer to actual data */ 238 | cpkthdr->data = data + MT_CPHEADER_LEN; 239 | 240 | /* Remember old position, for next call */ 241 | int_pos += cpkthdr->length + MT_CPHEADER_LEN; 242 | 243 | /* Read data successfully */ 244 | return 1; 245 | 246 | } else { 247 | /* Mark data as raw terminal data */ 248 | cpkthdr->cptype = MT_CPTYPE_PLAINDATA; 249 | cpkthdr->length = int_data_len - int_pos; 250 | cpkthdr->data = data; 251 | 252 | /* Consume the whole rest of the packet */ 253 | int_pos = int_data_len; 254 | 255 | /* Read data successfully */ 256 | return 1; 257 | } 258 | } 259 | 260 | int mndp_init_packet(struct mt_packet *packet, unsigned char version, unsigned char ttl) { 261 | struct mt_mndp_hdr *header = (struct mt_mndp_hdr *)packet->data; 262 | 263 | header->version = version; 264 | header->ttl = ttl; 265 | header->cksum = 0; 266 | 267 | packet->size = sizeof(*header); 268 | 269 | return sizeof(*header); 270 | } 271 | 272 | int mndp_add_attribute(struct mt_packet *packet, enum mt_mndp_attrtype attrtype, void *attrdata, 273 | unsigned short data_len) { 274 | unsigned char *data = packet->data + packet->size; 275 | unsigned short type = attrtype; 276 | unsigned short len = data_len; 277 | 278 | /* Something is really wrong. Packets should never become over 1500 bytes */ 279 | if (data_len > MT_PACKET_LEN - 4 - packet->size) { 280 | fprintf(stderr, _("mndp_add_attribute: ERROR, too large packet. Exceeds %d bytes\n"), MT_PACKET_LEN); 281 | return -1; 282 | } 283 | 284 | type = htons(type); 285 | memcpy(data, &type, sizeof(type)); 286 | 287 | len = htons(len); 288 | memcpy(data + 2, &len, sizeof(len)); 289 | 290 | memcpy(data + 4, attrdata, data_len); 291 | 292 | packet->size += 4 + data_len; 293 | 294 | return 4 + data_len; 295 | } 296 | 297 | struct mt_mndp_info *parse_mndp(const unsigned char *data, const int packet_len) { 298 | const unsigned char *p; 299 | static struct mt_mndp_info packet; 300 | struct mt_mndp_info *packetp = &packet; 301 | struct mt_mndp_hdr *mndp_hdr; 302 | 303 | /* Check for valid packet length */ 304 | if (packet_len < 18) { 305 | return NULL; 306 | } 307 | 308 | bzero(packetp, sizeof(*packetp)); 309 | 310 | mndp_hdr = (struct mt_mndp_hdr *)data; 311 | 312 | memcpy(&packetp->header, mndp_hdr, sizeof(struct mt_mndp_hdr)); 313 | 314 | p = data + sizeof(struct mt_mndp_hdr); 315 | 316 | while (p + 4 < data + packet_len) { 317 | unsigned short type, len; 318 | 319 | memcpy(&type, p, 2); 320 | memcpy(&len, p + 2, 2); 321 | 322 | type = ntohs(type); 323 | len = ntohs(len); 324 | 325 | p += 4; 326 | 327 | /* Check if len is invalid */ 328 | if (p + len > data + packet_len) { 329 | fprintf(stderr, 330 | _("%s: invalid data: " 331 | "%p + %u > %p + %d\n"), 332 | __func__, p, len, data, packet_len); 333 | break; 334 | } 335 | 336 | switch (type) { 337 | case MT_MNDPTYPE_ADDRESS: 338 | if (len >= ETH_ALEN) { 339 | memcpy(packetp->address, p, ETH_ALEN); 340 | } 341 | break; 342 | 343 | case MT_MNDPTYPE_IDENTITY: 344 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 345 | len = MT_MNDP_MAX_STRING_SIZE - 1; 346 | } 347 | 348 | memcpy(packetp->identity, p, len); 349 | packetp->identity[len] = '\0'; 350 | break; 351 | 352 | case MT_MNDPTYPE_PLATFORM: 353 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 354 | len = MT_MNDP_MAX_STRING_SIZE - 1; 355 | } 356 | 357 | memcpy(packetp->platform, p, len); 358 | packetp->platform[len] = '\0'; 359 | break; 360 | 361 | case MT_MNDPTYPE_VERSION: 362 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 363 | len = MT_MNDP_MAX_STRING_SIZE - 1; 364 | } 365 | 366 | memcpy(packetp->version, p, len); 367 | packetp->version[len] = '\0'; 368 | break; 369 | 370 | case MT_MNDPTYPE_TIMESTAMP: 371 | if (len >= 4) { 372 | memcpy(&packetp->uptime, p, 4); 373 | /* Seems like ping uptime is transmitted as little endian? */ 374 | packetp->uptime = le32toh(packetp->uptime); 375 | } 376 | break; 377 | 378 | case MT_MNDPTYPE_HARDWARE: 379 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 380 | len = MT_MNDP_MAX_STRING_SIZE - 1; 381 | } 382 | 383 | memcpy(packetp->hardware, p, len); 384 | packetp->hardware[len] = '\0'; 385 | break; 386 | 387 | case MT_MNDPTYPE_SOFTID: 388 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 389 | len = MT_MNDP_MAX_STRING_SIZE - 1; 390 | } 391 | 392 | memcpy(packetp->softid, p, len); 393 | packetp->softid[len] = '\0'; 394 | break; 395 | 396 | case MT_MNDPTYPE_IFNAME: 397 | if (len >= MT_MNDP_MAX_STRING_SIZE) { 398 | len = MT_MNDP_MAX_STRING_SIZE - 1; 399 | } 400 | 401 | memcpy(packetp->ifname, p, len); 402 | packetp->ifname[len] = '\0'; 403 | break; 404 | 405 | /*default: 406 | Unhandled MNDP type 407 | */ 408 | } 409 | 410 | p += len; 411 | } 412 | 413 | return packetp; 414 | } 415 | 416 | int query_mndp(const char *identity, unsigned char *mac) { 417 | int fastlookup = 0; 418 | int sock, length; 419 | int optval = 1; 420 | struct sockaddr_in si_me, si_remote; 421 | unsigned char buff[MT_PACKET_LEN]; 422 | unsigned int message = 0; 423 | struct timeval timeout; 424 | time_t start_time; 425 | fd_set read_fds; 426 | struct mt_mndp_info *packet; 427 | 428 | start_time = time(0); 429 | 430 | /* Open a UDP socket handle */ 431 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 432 | 433 | /* Allow to share socket */ 434 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 435 | 436 | /* Set initialize address/port */ 437 | memset((char *)&si_me, 0, sizeof(si_me)); 438 | si_me.sin_family = AF_INET; 439 | si_me.sin_port = htons(MT_MNDP_PORT); 440 | si_me.sin_addr.s_addr = htonl(INADDR_ANY); 441 | 442 | /* Bind to specified address/port */ 443 | if (bind(sock, (struct sockaddr *)&si_me, sizeof(si_me)) == -1) { 444 | fprintf(stderr, _("Error binding to %s:%d\n"), inet_ntoa(si_me.sin_addr), MT_MNDP_PORT); 445 | close(sock); 446 | return 0; 447 | } 448 | 449 | /* Set the socket to allow sending broadcast packets */ 450 | setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); 451 | 452 | /* Request routers identify themselves */ 453 | memset((char *)&si_remote, 0, sizeof(si_remote)); 454 | si_remote.sin_family = AF_INET; 455 | si_remote.sin_port = htons(MT_MNDP_PORT); 456 | si_remote.sin_addr.s_addr = htonl(INADDR_BROADCAST); 457 | 458 | if (sendto(sock, &message, sizeof(message), 0, (struct sockaddr *)&si_remote, sizeof(si_remote)) == -1) { 459 | fprintf(stderr, _("Unable to send broadcast packet: Router lookup will be slow\n")); 460 | fastlookup = 0; 461 | } else { 462 | fastlookup = 1; 463 | } 464 | 465 | while (1) { 466 | /* Timeout, in case we receive a lot of packets, but from the wrong routers */ 467 | if (time(0) - start_time > (fastlookup ? MT_MNDP_TIMEOUT : MT_MNDP_LONGTIMEOUT)) { 468 | goto done; 469 | } 470 | 471 | FD_ZERO(&read_fds); 472 | FD_SET(sock, &read_fds); 473 | 474 | timeout.tv_sec = fastlookup ? MT_MNDP_TIMEOUT : MT_MNDP_LONGTIMEOUT; 475 | timeout.tv_usec = 0; 476 | 477 | select(sock + 1, &read_fds, NULL, NULL, &timeout); 478 | if (!FD_ISSET(sock, &read_fds)) { 479 | goto done; 480 | } 481 | 482 | /* Read UDP packet */ 483 | length = recvfrom(sock, buff, sizeof(buff), 0, 0, 0); 484 | if (length < 0) { 485 | goto done; 486 | } 487 | 488 | /* Parse MNDP packet */ 489 | packet = parse_mndp(buff, length); 490 | 491 | if (packet != NULL) { 492 | if (strcasecmp(identity, packet->identity) == 0) { 493 | memcpy(mac, packet->address, ETH_ALEN); 494 | close(sock); 495 | return 1; 496 | } 497 | } 498 | } 499 | done: 500 | close(sock); 501 | return 0; 502 | } 503 | 504 | /* 505 | * This function accepts either a full MAC address using : or - as seperators. 506 | * Or a router hostname. The hostname will be searched for via MNDP broadcast packets. 507 | */ 508 | int query_mndp_or_mac(char *address, unsigned char *dstmac, int verbose) { 509 | char *p = address; 510 | int colons = 0; 511 | int dashs = 0; 512 | 513 | while (*p++) { 514 | if (*p == ':') { 515 | colons++; 516 | } else if (*p == '-') { 517 | dashs++; 518 | } 519 | } 520 | 521 | /* 522 | * Windows users often enter macs with dash instead 523 | * of colon. 524 | */ 525 | if (colons == 0 && dashs == 5) { 526 | p = address; 527 | while (*p++) { 528 | if (*p == '-') { 529 | *p = ':'; 530 | } 531 | } 532 | colons = dashs; 533 | } 534 | 535 | if (colons != 5) { 536 | /* 537 | * Not a valid mac-address. 538 | * Search for Router by identity name, using MNDP 539 | */ 540 | if (verbose) { 541 | fprintf(stderr, _("Searching for '%s'..."), address); 542 | } 543 | if (!query_mndp(address, dstmac)) { 544 | if (verbose) { 545 | fprintf(stderr, _("not found\n")); 546 | } 547 | return 0; 548 | } 549 | 550 | /* Router found, display mac and continue */ 551 | if (verbose) { 552 | fprintf(stderr, _("found\n")); 553 | } 554 | } else { 555 | /* Convert mac address string to ether_addr struct */ 556 | #if defined(__APPLE__) 557 | struct ether_addr *dstmac_buf = ether_aton(address); 558 | memcpy(dstmac, dstmac_buf, sizeof(struct ether_addr)); 559 | #else 560 | ether_aton_r(address, (struct ether_addr *)dstmac); 561 | #endif 562 | } 563 | 564 | return 1; 565 | } 566 | -------------------------------------------------------------------------------- /src/mtwei.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2022, Yandex 4 | Copyright (C) 2024, Google 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | /* 21 | Independent implementation of the Elliptic Curve Secure Remote Protocol 22 | (EC-SRP) key sharing and authentication protocol. 23 | 24 | This code implements the EC-SRP Algorithm defined in IEEE P1363.2 draft, 25 | whose text is available at 26 | https://web.archive.org/web/20131228182531/http://grouper.ieee.org/groups/1363/passwdPK/submissions/p1363ecsrp.pdf 27 | 28 | The code is derived from the text of the RFC and another PoC Python 29 | implementation from Margin Research 30 | https://github.com/MarginResearch/mikrotik_authentication 31 | */ 32 | #include "mtwei.h" 33 | #include "config.h" 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #if defined(HAVE_SYS_RANDOM_H) 40 | #include 41 | #endif 42 | 43 | #define _(STRING) gettext(STRING) 44 | 45 | #if !defined(HAVE_GETRANDOM) && defined(HAVE_ARC4RANDOM) 46 | int getrandom(char *buf, size_t size, int flags) { 47 | arc4random_buf(buf, size); 48 | return size; 49 | } 50 | #endif 51 | 52 | // assert, output message to stderr, and jump to abort label for cleanup 53 | #define CHECKNULL(exp) \ 54 | do { \ 55 | if ((exp) == 0) { \ 56 | fprintf(stderr, _("FATAL ERROR: Function returned NULL at %s:%d: %s;\n"), __FILE__, __LINE__, #exp); \ 57 | goto abort; \ 58 | } \ 59 | } while (0) 60 | 61 | void mtwei_init(mtwei_state_t *state) { 62 | BIGNUM *a = NULL, *b = NULL, *gx = NULL, *gy = NULL; 63 | BIGNUM *cofactor = NULL; 64 | 65 | CHECKNULL(cofactor = BN_new()); 66 | CHECKNULL(state->ctx = BN_CTX_new()); 67 | 68 | state->mod = NULL; 69 | CHECKNULL(BN_hex2bn(&state->mod, "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed")); 70 | 71 | CHECKNULL(BN_hex2bn(&a, "2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa984914a144")); 72 | CHECKNULL(BN_hex2bn(&b, "7b425ed097b425ed097b425ed097b425ed097b425ed097b4260b5e9c7710c864")); 73 | CHECKNULL(BN_hex2bn(&gx, "2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad245a")); 74 | CHECKNULL(BN_hex2bn(&gy, "5f51e65e475f794b1fe122d388b72eb36dc2b28192839e4dd6163a5d81312c14")); 75 | 76 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 77 | CHECKNULL(state->curve25519 = EC_GROUP_new_curve_GFp(state->mod, a, b, 0)); 78 | #else 79 | CHECKNULL(state->curve25519 = EC_GROUP_new(EC_GFp_simple_method())); 80 | #endif 81 | CHECKNULL(state->g = EC_POINT_new(state->curve25519)); 82 | 83 | CHECKNULL(state->order = BN_new()); 84 | CHECKNULL(BN_hex2bn(&state->order, "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed")); 85 | 86 | BN_set_word(cofactor, 8); 87 | state->w2m = NULL; 88 | state->m2w = NULL; 89 | CHECKNULL(BN_hex2bn(&state->w2m, "555555555555555555555555555555555555555555555555555555555552db9c")); 90 | CHECKNULL(BN_hex2bn(&state->m2w, "2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad2451")); 91 | 92 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 93 | CHECKNULL(EC_POINT_set_affine_coordinates(state->curve25519, state->g, gx, gy, 0)); 94 | #else 95 | CHECKNULL(EC_GROUP_set_curve_GFp(state->curve25519, state->mod, a, b, 0)); 96 | CHECKNULL(EC_POINT_set_affine_coordinates_GFp(state->curve25519, state->g, gx, gy, 0)); 97 | #endif 98 | CHECKNULL(EC_GROUP_set_generator(state->curve25519, state->g, state->order, cofactor)); 99 | 100 | BN_clear_free(a); 101 | BN_clear_free(b); 102 | BN_clear_free(gx); 103 | BN_clear_free(gy); 104 | BN_clear_free(cofactor); 105 | 106 | return; 107 | 108 | abort: 109 | BN_clear_free(cofactor); 110 | BN_CTX_free(state->ctx); 111 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 112 | EC_GROUP_free(state->curve25519); 113 | #else 114 | EC_GROUP_clear_free(state->curve25519); 115 | #endif 116 | EC_POINT_clear_free(state->g); 117 | BN_clear_free(state->mod); 118 | BN_clear_free(state->order); 119 | BN_clear_free(state->w2m); 120 | BN_clear_free(state->m2w); 121 | BN_clear_free(a); 122 | BN_clear_free(b); 123 | BN_clear_free(gx); 124 | BN_clear_free(gy); 125 | abort(); 126 | } 127 | 128 | static BIGNUM *tangle(mtwei_state_t *state, EC_POINT *target, uint8_t *validator, int negate) { 129 | EC_POINT *validator_pt = NULL; 130 | BIGNUM *v = NULL, *vpub_x = NULL, *edpx = NULL, *edpxm = NULL; 131 | 132 | CHECKNULL(validator_pt = EC_POINT_new(state->curve25519)); 133 | 134 | CHECKNULL(v = BN_bin2bn(validator, 32, NULL)); 135 | EC_POINT_mul(state->curve25519, validator_pt, NULL, state->g, v, state->ctx); 136 | 137 | CHECKNULL(vpub_x = BN_new()); 138 | 139 | unsigned char buf_out[32]; 140 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 141 | EC_POINT_get_affine_coordinates(state->curve25519, validator_pt, vpub_x, NULL, NULL); 142 | #else 143 | EC_POINT_get_affine_coordinates_GFp(state->curve25519, validator_pt, vpub_x, NULL, NULL); 144 | #endif 145 | BN_mod_add(vpub_x, vpub_x, state->w2m, state->mod, state->ctx); 146 | BN_bn2binpad(vpub_x, buf_out, 32); 147 | 148 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 149 | EVP_Digest(buf_out, 32, buf_out, NULL, EVP_sha256(), NULL); 150 | #else 151 | SHA256_CTX keys; 152 | SHA256_Init(&keys); 153 | SHA256_Update(&keys, buf_out, 32); 154 | SHA256_Final(buf_out, &keys); 155 | #endif 156 | 157 | CHECKNULL(edpx = BN_bin2bn(buf_out, 32, NULL)); 158 | CHECKNULL(edpxm = BN_new()); 159 | 160 | while (1) { 161 | BN_bn2binpad(edpx, buf_out, 32); 162 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 163 | EVP_Digest(buf_out, 32, buf_out, NULL, EVP_sha256(), NULL); 164 | #else 165 | SHA256_Init(&keys); 166 | SHA256_Update(&keys, buf_out, 32); 167 | SHA256_Final(buf_out, &keys); 168 | #endif 169 | BN_bin2bn(buf_out, 32, edpxm); 170 | BN_mod_add(edpxm, edpxm, state->m2w, state->mod, state->ctx); 171 | if (EC_POINT_set_compressed_coordinates(state->curve25519, validator_pt, edpxm, negate, state->ctx) == 1) { 172 | break; 173 | } 174 | BN_add_word(edpx, 1); 175 | } 176 | 177 | if (!EC_POINT_add(state->curve25519, target, target, validator_pt, state->ctx)) { 178 | fprintf(stderr, _("Cannot mix gamma into pubkey: %s\n"), ERR_error_string(ERR_get_error(), NULL)); 179 | abort(); 180 | } 181 | 182 | EC_POINT_clear_free(validator_pt); 183 | BN_clear_free(vpub_x); 184 | BN_clear_free(edpxm); 185 | 186 | return v; 187 | 188 | abort: 189 | EC_POINT_clear_free(validator_pt); 190 | BN_clear_free(vpub_x); 191 | BN_clear_free(edpx); 192 | BN_clear_free(edpxm); 193 | abort(); 194 | } 195 | 196 | BIGNUM *mtwei_keygen(mtwei_state_t *state, uint8_t *pubkey_out, uint8_t *validator) { 197 | uint8_t client_priv[32]; 198 | EC_POINT *pubkey = NULL; 199 | BIGNUM *x = NULL, *y = NULL, *privkey = NULL; 200 | 201 | CHECKNULL(pubkey = EC_POINT_new(state->curve25519)); 202 | 203 | CHECKNULL(x = BN_new()); 204 | CHECKNULL(y = BN_new()); 205 | 206 | if (getrandom((char *)client_priv, sizeof(client_priv), 0) != sizeof(client_priv)) { 207 | perror("getrandom"); 208 | goto abort; 209 | } 210 | client_priv[0] &= 248; 211 | client_priv[31] &= 127; 212 | client_priv[31] |= 64; 213 | 214 | CHECKNULL(privkey = BN_bin2bn(client_priv, sizeof(client_priv), NULL)); 215 | if (!EC_POINT_mul(state->curve25519, pubkey, NULL, state->g, privkey, state->ctx)) { 216 | fprintf(stderr, _("Cannot make a public key: %s\n"), ERR_error_string(ERR_get_error(), NULL)); 217 | goto abort; 218 | } 219 | 220 | if (validator != NULL) { 221 | CHECKNULL(tangle(state, pubkey, validator, 0)); 222 | } 223 | 224 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 225 | EC_POINT_get_affine_coordinates(state->curve25519, pubkey, x, y, NULL); 226 | #else 227 | EC_POINT_get_affine_coordinates_GFp(state->curve25519, pubkey, x, y, NULL); 228 | #endif 229 | BN_mod_add(x, x, state->w2m, state->mod, state->ctx); 230 | BN_bn2binpad(x, pubkey_out, 32); 231 | pubkey_out[32] = BN_is_odd(y) ? 1 : 0; 232 | 233 | EC_POINT_clear_free(pubkey); 234 | BN_clear_free(x); 235 | BN_clear_free(y); 236 | 237 | return privkey; 238 | 239 | abort: 240 | EC_POINT_clear_free(pubkey); 241 | BN_clear_free(x); 242 | BN_clear_free(y); 243 | BN_clear_free(privkey); 244 | abort(); 245 | } 246 | 247 | void mtwei_id(const char *username, const char *password, const unsigned char *salt, uint8_t *validator_out) { 248 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 249 | EVP_MD_CTX *mdctx; 250 | mdctx = EVP_MD_CTX_new(); 251 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 252 | EVP_DigestUpdate(mdctx, username, strlen(username)); 253 | EVP_DigestUpdate(mdctx, ":", 1); 254 | EVP_DigestUpdate(mdctx, password, strlen(password)); 255 | EVP_DigestFinal_ex(mdctx, validator_out, NULL); 256 | 257 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 258 | EVP_DigestUpdate(mdctx, salt, 16); 259 | EVP_DigestUpdate(mdctx, validator_out, SHA256_DIGEST_LENGTH); 260 | EVP_DigestFinal_ex(mdctx, validator_out, NULL); 261 | 262 | EVP_MD_CTX_free(mdctx); 263 | #else 264 | SHA256_CTX v, v1; 265 | SHA256_Init(&v1); 266 | SHA256_Update(&v1, username, strlen(username)); 267 | SHA256_Update(&v1, ":", 1); 268 | SHA256_Update(&v1, password, strlen(password)); 269 | SHA256_Final(validator_out, &v1); 270 | 271 | SHA256_Init(&v); 272 | SHA256_Update(&v, salt, 16); 273 | SHA256_Update(&v, validator_out, SHA256_DIGEST_LENGTH); 274 | SHA256_Final(validator_out, &v); 275 | #endif 276 | } 277 | 278 | void mtwei_docrypto(mtwei_state_t *state, BIGNUM *privkey, const uint8_t *server_key, const uint8_t *client_key, 279 | uint8_t *validator, uint8_t *buf_out) { 280 | EC_POINT *pub = NULL, *server_pubkey = NULL, *pt = NULL; 281 | BIGNUM *server_pubkey_x = NULL, *v = NULL, *vh = NULL, *pt_x = NULL, *z_input = NULL; 282 | 283 | CHECKNULL(pub = EC_POINT_new(state->curve25519)); 284 | EC_POINT_mul(state->curve25519, pub, NULL, state->g, privkey, state->ctx); 285 | 286 | CHECKNULL(server_pubkey = EC_POINT_new(state->curve25519)); 287 | CHECKNULL(server_pubkey_x = BN_bin2bn(server_key, 32, NULL)); 288 | BN_mod_add(server_pubkey_x, server_pubkey_x, state->m2w, state->mod, state->ctx); 289 | if (EC_POINT_set_compressed_coordinates(state->curve25519, server_pubkey, server_pubkey_x, server_key[32], 290 | state->ctx) != 1) { 291 | fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); 292 | goto abort; 293 | } 294 | 295 | SHA256_CTX keys; 296 | CHECKNULL(v = tangle(state, server_pubkey, validator, 1)); 297 | 298 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 299 | EVP_MD_CTX *mdctx; 300 | mdctx = EVP_MD_CTX_new(); 301 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 302 | EVP_DigestUpdate(mdctx, client_key, 32); 303 | EVP_DigestUpdate(mdctx, server_key, 32); 304 | EVP_DigestFinal_ex(mdctx, buf_out, NULL); 305 | EVP_MD_CTX_free(mdctx); 306 | #else 307 | SHA256_Init(&keys); 308 | SHA256_Update(&keys, client_key, 32); 309 | SHA256_Update(&keys, server_key, 32); 310 | SHA256_Final(buf_out, &keys); 311 | #endif 312 | 313 | CHECKNULL(vh = BN_bin2bn(buf_out, 32, NULL)); 314 | 315 | BN_mod_mul(vh, v, vh, state->order, state->ctx); 316 | BN_mod_add(vh, vh, privkey, state->order, state->ctx); 317 | 318 | CHECKNULL(pt = EC_POINT_new(state->curve25519)); 319 | EC_POINT_mul(state->curve25519, pt, NULL, server_pubkey, vh, state->ctx); 320 | 321 | CHECKNULL(pt_x = BN_new()); 322 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 323 | EC_POINT_get_affine_coordinates(state->curve25519, pt, pt_x, NULL, NULL); 324 | #else 325 | EC_POINT_get_affine_coordinates_GFp(state->curve25519, pt, pt_x, NULL, NULL); 326 | #endif 327 | 328 | CHECKNULL(z_input = BN_new()); 329 | BN_mod_add(z_input, pt_x, state->w2m, state->mod, state->ctx); 330 | 331 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 332 | mdctx = EVP_MD_CTX_new(); 333 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 334 | EVP_DigestUpdate(mdctx, buf_out, 32); 335 | BN_bn2binpad(z_input, buf_out, 32); 336 | EVP_DigestUpdate(mdctx, buf_out, 32); 337 | EVP_DigestFinal_ex(mdctx, buf_out, NULL); 338 | EVP_MD_CTX_free(mdctx); 339 | #else 340 | SHA256_Init(&keys); 341 | SHA256_Update(&keys, buf_out, 32); 342 | BN_bn2binpad(z_input, buf_out, 32); 343 | SHA256_Update(&keys, buf_out, 32); 344 | SHA256_Final(buf_out, &keys); 345 | #endif 346 | 347 | EC_POINT_clear_free(pub); 348 | EC_POINT_clear_free(server_pubkey); 349 | BN_clear_free(server_pubkey_x); 350 | BN_clear_free(v); 351 | BN_clear_free(vh); 352 | EC_POINT_clear_free(pt); 353 | BN_clear_free(pt_x); 354 | BN_clear_free(z_input); 355 | 356 | return; 357 | 358 | abort: 359 | EC_POINT_clear_free(pub); 360 | EC_POINT_clear_free(server_pubkey); 361 | BN_clear_free(server_pubkey_x); 362 | BN_clear_free(v); 363 | BN_clear_free(vh); 364 | EC_POINT_clear_free(pt); 365 | BN_clear_free(pt_x); 366 | BN_clear_free(z_input); 367 | abort(); 368 | } 369 | 370 | void mtwei_docryptos(mtwei_state_t *state, BIGNUM *privkey, const uint8_t *client_key, const uint8_t *server_key, 371 | uint8_t *validator, uint8_t *buf_out) { 372 | EC_POINT *pub = NULL, *client_pubkey = NULL, *validator_pt = NULL, *hv = NULL, *pt = NULL; 373 | BIGNUM *client_pubkey_x = NULL, *v = NULL, *h = NULL, *pt_x = NULL, *z_input = NULL; 374 | 375 | CHECKNULL(pub = EC_POINT_new(state->curve25519)); 376 | EC_POINT_mul(state->curve25519, pub, NULL, state->g, privkey, state->ctx); 377 | 378 | CHECKNULL(client_pubkey = EC_POINT_new(state->curve25519)); 379 | CHECKNULL(client_pubkey_x = BN_bin2bn(client_key, 32, NULL)); 380 | BN_mod_add(client_pubkey_x, client_pubkey_x, state->m2w, state->mod, state->ctx); 381 | if (EC_POINT_set_compressed_coordinates(state->curve25519, client_pubkey, client_pubkey_x, client_key[32], 382 | state->ctx) != 1) { 383 | fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); 384 | goto abort; 385 | } 386 | 387 | SHA256_CTX keys; 388 | CHECKNULL(v = BN_bin2bn(validator, 32, NULL)); 389 | 390 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 391 | EVP_MD_CTX *mdctx; 392 | mdctx = EVP_MD_CTX_new(); 393 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 394 | EVP_DigestUpdate(mdctx, client_key, 32); 395 | EVP_DigestUpdate(mdctx, server_key, 32); 396 | EVP_DigestFinal_ex(mdctx, buf_out, NULL); 397 | EVP_MD_CTX_free(mdctx); 398 | #else 399 | SHA256_Init(&keys); 400 | SHA256_Update(&keys, client_key, 32); 401 | SHA256_Update(&keys, server_key, 32); 402 | SHA256_Final(buf_out, &keys); 403 | #endif 404 | 405 | CHECKNULL(validator_pt = EC_POINT_new(state->curve25519)); 406 | EC_POINT_mul(state->curve25519, validator_pt, NULL, state->g, v, state->ctx); 407 | 408 | CHECKNULL(h = BN_bin2bn(buf_out, 32, NULL)); 409 | CHECKNULL(hv = EC_POINT_new(state->curve25519)); 410 | 411 | EC_POINT_mul(state->curve25519, hv, NULL, validator_pt, h, state->ctx); 412 | EC_POINT_add(state->curve25519, client_pubkey, client_pubkey, hv, state->ctx); 413 | 414 | CHECKNULL(pt = EC_POINT_new(state->curve25519)); 415 | EC_POINT_mul(state->curve25519, pt, NULL, client_pubkey, privkey, state->ctx); 416 | 417 | CHECKNULL(pt_x = BN_new()); 418 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 419 | EC_POINT_get_affine_coordinates(state->curve25519, pt, pt_x, NULL, NULL); 420 | #else 421 | EC_POINT_get_affine_coordinates_GFp(state->curve25519, pt, pt_x, NULL, NULL); 422 | #endif 423 | CHECKNULL(z_input = BN_new()); 424 | BN_mod_add(z_input, pt_x, state->w2m, state->mod, state->ctx); 425 | 426 | #if OPENSSL_VERSION_NUMBER >= 0x030000000 // 3.0.0 427 | mdctx = EVP_MD_CTX_new(); 428 | EVP_DigestInit_ex2(mdctx, EVP_sha256(), NULL); 429 | EVP_DigestUpdate(mdctx, buf_out, 32); 430 | BN_bn2binpad(z_input, buf_out, 32); 431 | EVP_DigestUpdate(mdctx, buf_out, 32); 432 | EVP_DigestFinal_ex(mdctx, buf_out, NULL); 433 | EVP_MD_CTX_free(mdctx); 434 | #else 435 | SHA256_Init(&keys); 436 | SHA256_Update(&keys, buf_out, 32); 437 | BN_bn2binpad(z_input, buf_out, 32); 438 | SHA256_Update(&keys, buf_out, 32); 439 | SHA256_Final(buf_out, &keys); 440 | #endif 441 | 442 | EC_POINT_clear_free(pub); 443 | EC_POINT_clear_free(client_pubkey); 444 | BN_clear_free(client_pubkey_x); 445 | BN_clear_free(v); 446 | EC_POINT_clear_free(validator_pt); 447 | BN_clear_free(h); 448 | EC_POINT_clear_free(hv); 449 | EC_POINT_clear_free(pt); 450 | BN_clear_free(pt_x); 451 | BN_clear_free(z_input); 452 | 453 | return; 454 | 455 | abort: 456 | EC_POINT_clear_free(pub); 457 | EC_POINT_clear_free(client_pubkey); 458 | BN_clear_free(client_pubkey_x); 459 | BN_clear_free(v); 460 | EC_POINT_clear_free(validator_pt); 461 | BN_clear_free(h); 462 | EC_POINT_clear_free(hv); 463 | EC_POINT_clear_free(pt); 464 | BN_clear_free(pt_x); 465 | BN_clear_free(z_input); 466 | abort(); 467 | } 468 | -------------------------------------------------------------------------------- /src/interfaces.c: -------------------------------------------------------------------------------- 1 | /* 2 | Mac-Telnet - Connect to RouterOS or mactelnetd devices via MAC address 3 | Copyright (C) 2010, Håkon Nessjøen 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include "config.h" 20 | #if defined(__FreeBSD__) 21 | #define __USE_BSD 22 | #define __FAVOR_BSD 23 | #endif 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #if defined(__FreeBSD__) || defined(__APPLE__) 35 | #include 36 | #endif 37 | #include 38 | #include 39 | #if defined(__FreeBSD__) || defined(__APPLE__) 40 | #include 41 | #define ETH_FRAME_LEN (ETHER_MAX_LEN - ETHER_CRC_LEN) 42 | #define ETH_ALEN ETHER_ADDR_LEN 43 | #else 44 | #include 45 | #endif 46 | #ifdef HAVE_LINUX_NETLINK_H 47 | #include 48 | #include 49 | #endif 50 | #ifdef __APPLE__ 51 | #include 52 | #include 53 | #endif 54 | #include 55 | #include 56 | #include 57 | #include 58 | #ifndef __linux__ 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #else 65 | #include 66 | #endif 67 | 68 | #include "protocol.h" 69 | #include "interfaces.h" 70 | #include "utlist.h" 71 | 72 | #define _(STRING) gettext(STRING) 73 | 74 | struct net_interface *net_get_interface_ptr(struct net_interface **interfaces, char *name, int create) { 75 | struct net_interface *interface; 76 | 77 | DL_FOREACH(*interfaces, interface) { 78 | if (strncmp(interface->name, name, 254) == 0) { 79 | return interface; 80 | } 81 | } 82 | 83 | if (create) { 84 | interface = (struct net_interface *)calloc(1, sizeof(struct net_interface)); 85 | if (interface == NULL) { 86 | fprintf(stderr, _("Unable to allocate memory for interface\n")); 87 | exit(1); 88 | } 89 | strncpy(interface->name, name, 254); 90 | interface->name[254] = '\0'; 91 | DL_APPEND(*interfaces, interface); 92 | return interface; 93 | } 94 | 95 | return NULL; 96 | } 97 | 98 | #ifdef __linux__ 99 | static void net_update_mac(struct net_interface *interfaces) { 100 | unsigned char emptymac[] = {0, 0, 0, 0, 0, 0}; 101 | struct ifreq ifr; 102 | int tmpsock; 103 | struct net_interface *interface; 104 | 105 | tmpsock = socket(PF_INET, SOCK_DGRAM, 0); 106 | if (tmpsock < 0) { 107 | perror("net_update_mac"); 108 | exit(1); 109 | } 110 | DL_FOREACH(interfaces, interface) { 111 | /* Find interface hardware address from device_name */ 112 | strncpy(ifr.ifr_name, interface->name, 16); 113 | 114 | if (ioctl(tmpsock, SIOCGIFFLAGS, &ifr)) 115 | continue; 116 | 117 | if (!(ifr.ifr_flags & IFF_UP)) 118 | continue; 119 | 120 | if (ioctl(tmpsock, SIOCGIFHWADDR, &ifr) == 0) { 121 | /* Fetch mac address */ 122 | memcpy(interface->mac_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 123 | if (memcmp(interface->mac_addr, &emptymac, ETH_ALEN) != 0) { 124 | interface->has_mac = 1; 125 | } 126 | } 127 | } 128 | close(tmpsock); 129 | } 130 | 131 | static int get_device_index(char *device_name) { 132 | struct ifreq ifr; 133 | int tmpsock; 134 | 135 | tmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 136 | 137 | /* Find interface index from device_name */ 138 | strncpy(ifr.ifr_name, device_name, 16); 139 | if (ioctl(tmpsock, SIOCGIFINDEX, &ifr) != 0) { 140 | return -1; 141 | } 142 | 143 | /* Return interface index */ 144 | return ifr.ifr_ifindex; 145 | } 146 | #endif 147 | 148 | int net_get_interfaces(struct net_interface **interfaces) { 149 | static struct ifaddrs *int_addrs; 150 | static const struct ifaddrs *ifaddrsp; 151 | const struct sockaddr_in *dl_addr; 152 | int found = 0; 153 | #if !defined(__FreeBSD__) 154 | uint32_t allones_bcast = htonl(INADDR_BROADCAST); 155 | #endif 156 | 157 | if (getifaddrs(&int_addrs) < 0) { 158 | perror("getifaddrs"); 159 | exit(1); 160 | } 161 | 162 | for (ifaddrsp = int_addrs; ifaddrsp; ifaddrsp = ifaddrsp->ifa_next) { 163 | dl_addr = (const struct sockaddr_in *)ifaddrsp->ifa_addr; 164 | 165 | if (ifaddrsp->ifa_addr == NULL) 166 | continue; 167 | 168 | #ifdef __linux__ 169 | if (ifaddrsp->ifa_addr->sa_family == AF_INET || ifaddrsp->ifa_addr->sa_family == AF_PACKET) { 170 | #else 171 | if (ifaddrsp->ifa_addr->sa_family == AF_INET) { 172 | #endif 173 | struct net_interface *interface = net_get_interface_ptr(interfaces, ifaddrsp->ifa_name, 1); 174 | if (interface != NULL) { 175 | found++; 176 | 177 | if (ifaddrsp->ifa_addr->sa_family == AF_INET) { 178 | memcpy(interface->ipv4_addr, &dl_addr->sin_addr, IPV4_ALEN); 179 | #if defined(__FreeBSD__) 180 | memcpy(interface->bcast_addr, &((const struct sockaddr_in *)ifaddrsp->ifa_broadaddr)->sin_addr, IPV4_ALEN); 181 | #else 182 | memcpy(interface->bcast_addr, &allones_bcast, IPV4_ALEN); 183 | #endif 184 | } else { 185 | memset(interface->ipv4_addr, 0, IPV4_ALEN); 186 | memset(interface->bcast_addr, 0, IPV4_ALEN); 187 | } 188 | } 189 | #ifdef __linux__ 190 | interface->ifindex = get_device_index(interface->name); 191 | #endif 192 | } 193 | #ifndef __linux__ 194 | { 195 | unsigned char emptymac[] = {0, 0, 0, 0, 0, 0}; 196 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifaddrsp->ifa_addr; 197 | 198 | if (sdl->sdl_alen == ETH_ALEN) { 199 | struct net_interface *interface = net_get_interface_ptr(interfaces, ifaddrsp->ifa_name, 1); 200 | memcpy(interface->mac_addr, LLADDR(sdl), ETH_ALEN); 201 | if (interface != NULL && memcmp(interface->mac_addr, &emptymac, ETH_ALEN) != 0) { 202 | interface->has_mac = 1; 203 | } 204 | } 205 | } 206 | #endif 207 | } 208 | freeifaddrs(int_addrs); 209 | 210 | #ifdef __linux__ 211 | net_update_mac(*interfaces); 212 | #endif 213 | 214 | #if 0 215 | { 216 | struct net_interface *interface; 217 | DL_FOREACH(*interfaces, interface) { 218 | struct in_addr *addr = 219 | (struct in_addr *)interface->ipv4_addr; 220 | struct in_addr *bcast = 221 | (struct in_addr *)interface->bcast_addr; 222 | 223 | printf("Interface %s:\n", interface->name); 224 | printf("\tIP: %s\n", inet_ntoa(*addr)); 225 | printf("\tBCAST: %s\n", inet_ntoa(*bcast)); 226 | printf("\tMAC: %s\n", 227 | ether_ntoa((struct ether_addr *)interface->mac_addr)); 228 | #ifdef __linux__ 229 | printf("\tIfIndex: %d\n", interface->ifindex); 230 | #endif 231 | printf("\n"); 232 | } 233 | } 234 | #endif 235 | return found; 236 | } 237 | 238 | int should_refresh_interfaces() { 239 | static time_t last_refresh = 0; 240 | time_t now = time(NULL); 241 | 242 | if (now - last_refresh > 1) { 243 | last_refresh = now; 244 | return 1; 245 | } 246 | 247 | return 0; 248 | } 249 | 250 | #if defined(__linux__) && defined(FROM_MACTELNETD) && defined(HAVE_LINUX_NETLINK_H) 251 | 252 | int get_netlink_fd() { 253 | int fd; 254 | struct sockaddr_nl addr; 255 | struct nlmsghdr *nh; 256 | char buf[BUFSIZ]; 257 | 258 | fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 259 | if (fd < 0) { 260 | perror("netlink:socket"); 261 | exit(1); 262 | } 263 | memset(&addr, 0, sizeof(addr)); 264 | addr.nl_family = AF_NETLINK; 265 | addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; 266 | 267 | int err = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 268 | if (err < 0) { 269 | perror("netlink:bind"); 270 | exit(1); 271 | } 272 | 273 | return fd; 274 | } 275 | 276 | void read_netlink(int fd) { 277 | static char buf[BUFSIZ]; 278 | 279 | memset(buf, 0, sizeof(buf)); 280 | recv(fd, buf, sizeof(buf), 0); 281 | /* 282 | We ignore the result for now, we will just do a full refresh of the 283 | network interfaces 284 | */ 285 | } 286 | 287 | #endif 288 | 289 | #if defined(__APPLE__) && defined(FROM_MACTELNETD) 290 | 291 | void callback(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) { 292 | if (!should_refresh_interfaces()) { 293 | return; 294 | } 295 | 296 | syslog(LOG_NOTICE, _("Network change detected")); 297 | 298 | // Handle the network change 299 | pid_t pid = getpid(); 300 | kill(pid, SIGHUP); 301 | } 302 | 303 | void *init_network_watcher_thread(void *arg) { 304 | SCDynamicStoreRef store; 305 | CFRunLoopSourceRef source; 306 | 307 | store = SCDynamicStoreCreate(NULL, CFSTR("no.lunatic.mactelnet.NetworkChangeDetector"), callback, NULL); 308 | source = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 309 | 310 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); 311 | 312 | CFStringRef keysToMonitor[] = {CFSTR("State:/Network/Interface"), CFSTR("State:/Network/Global/IPv4")}; 313 | 314 | CFArrayRef keysArray = CFArrayCreate(NULL, (const void **)keysToMonitor, 315 | sizeof(keysToMonitor) / sizeof(keysToMonitor[0]), &kCFTypeArrayCallBacks); 316 | SCDynamicStoreSetNotificationKeys(store, keysArray, NULL); 317 | 318 | CFRunLoopRun(); 319 | 320 | CFRelease(store); 321 | CFRelease(source); 322 | 323 | return NULL; 324 | } 325 | 326 | void init_network_watcher() { 327 | pthread_t thread; 328 | int result = pthread_create(&thread, NULL, init_network_watcher_thread, NULL); 329 | if (result != 0) { 330 | fprintf(stderr, _("Error creating network watcher thread: %s\n"), strerror(result)); 331 | exit(EXIT_FAILURE); 332 | } 333 | } 334 | #endif 335 | 336 | unsigned short in_cksum(unsigned short *addr, int len) { 337 | int nleft = len; 338 | int sum = 0; 339 | unsigned short *w = addr; 340 | unsigned short answer = 0; 341 | 342 | while (nleft > 1) { 343 | sum += *w++; 344 | nleft -= 2; 345 | } 346 | 347 | if (nleft == 1) { 348 | *(unsigned char *)(&answer) = *(unsigned char *)w; 349 | sum += answer; 350 | } 351 | 352 | sum = (sum >> 16) + (sum & 0xFFFF); 353 | sum += (sum >> 16); 354 | answer = ~sum; 355 | return (answer); 356 | } 357 | 358 | unsigned short udp_sum_calc(unsigned char *src_addr, unsigned char *dst_addr, unsigned char *data, unsigned short len) { 359 | unsigned short prot_udp = 17; 360 | unsigned short padd = 0; 361 | unsigned short word16; 362 | unsigned int sum = 0; 363 | int i; 364 | 365 | /* Padding ? */ 366 | padd = (len % 2); 367 | if (padd) { 368 | data[len] = 0; 369 | } 370 | 371 | /* header+data */ 372 | for (i = 0; i < len + padd; i += 2) { 373 | word16 = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF); 374 | sum += word16; 375 | } 376 | 377 | /* source ip */ 378 | for (i = 0; i < IPV4_ALEN; i += 2) { 379 | word16 = ((src_addr[i] << 8) & 0xFF00) + (src_addr[i + 1] & 0xFF); 380 | sum += word16; 381 | } 382 | 383 | /* dest ip */ 384 | for (i = 0; i < IPV4_ALEN; i += 2) { 385 | word16 = ((dst_addr[i] << 8) & 0xFF00) + (dst_addr[i + 1] & 0xFF); 386 | sum += word16; 387 | } 388 | 389 | sum += prot_udp + len; 390 | 391 | while (sum >> 16) 392 | sum = (sum & 0xFFFF) + (sum >> 16); 393 | 394 | sum = ~sum; 395 | 396 | if (sum == 0) 397 | sum = 0xFFFF; 398 | 399 | return (unsigned short)sum; 400 | } 401 | 402 | #if !defined(__linux__) 403 | int net_find_bpf_device() { 404 | char dev[11]; 405 | for (int i = 0; i < 99; ++i) { 406 | sprintf(dev, "/dev/bpf%i", i); 407 | int fd = open(dev, O_RDWR); 408 | if (fd != -1) { 409 | return fd; 410 | } 411 | } 412 | return -1; 413 | } 414 | #endif 415 | 416 | int net_init_raw_socket() { 417 | int fd; 418 | 419 | #ifdef __linux__ 420 | /* Transmit raw packets with this socket */ 421 | fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 422 | if (fd < 0) { 423 | perror("raw_socket"); 424 | exit(1); 425 | } 426 | #else 427 | /* Transmit raw packets with bpf */ 428 | fd = net_find_bpf_device(); 429 | if (fd <= 0) { 430 | perror("open_bpf"); 431 | exit(1); 432 | } 433 | #endif 434 | 435 | return fd; 436 | } 437 | 438 | int net_send_udp(const int fd, struct net_interface *interface, const unsigned char *sourcemac, 439 | const unsigned char *destmac, const struct in_addr *sourceip, const int sourceport, 440 | const struct in_addr *destip, const int destport, const unsigned char *data, const int datalen) { 441 | #ifdef __linux__ 442 | struct sockaddr_ll socket_address; 443 | #endif 444 | /* 445 | * Create a buffer for the full ethernet frame 446 | * and align header pointers to the correct positions. 447 | */ 448 | static unsigned char stackbuf[ETH_FRAME_LEN]; 449 | void *buffer = (void *)&stackbuf; 450 | #if defined(__FreeBSD__) || defined(__APPLE__) 451 | struct ether_header *eh = (struct ether_header *)buffer; 452 | struct ip *ip = (struct ip *)(buffer + 14); 453 | #else 454 | struct ethhdr *eh = (struct ethhdr *)buffer; 455 | struct iphdr *ip = (struct iphdr *)(buffer + 14); 456 | #endif 457 | struct udphdr *udp = (struct udphdr *)(buffer + 14 + 20); 458 | unsigned char *rest = (unsigned char *)(buffer + 20 + 14 + sizeof(struct udphdr)); 459 | 460 | /* Avoid integer overflow in check */ 461 | if (datalen > ETH_FRAME_LEN - ((void *)rest - (void *)buffer)) { 462 | fprintf(stderr, _("packet size too large\n")); 463 | return 0; 464 | } 465 | 466 | static unsigned int id = 1; 467 | int send_result = 0; 468 | 469 | /* Abort if we couldn't allocate enough memory */ 470 | if (buffer == NULL) { 471 | perror("malloc"); 472 | exit(1); 473 | } 474 | 475 | /* Init ethernet header */ 476 | #if defined(__FreeBSD__) || defined(__APPLE__) 477 | memcpy(eh->ether_shost, sourcemac, ETH_ALEN); 478 | memcpy(eh->ether_dhost, destmac, ETH_ALEN); 479 | eh->ether_type = htons(ETHERTYPE_IP); 480 | #else 481 | memcpy(eh->h_source, sourcemac, ETH_ALEN); 482 | memcpy(eh->h_dest, destmac, ETH_ALEN); 483 | eh->h_proto = htons(ETH_P_IP); 484 | #endif 485 | 486 | #ifdef __linux__ 487 | /* Init SendTo struct */ 488 | socket_address.sll_family = AF_PACKET; 489 | socket_address.sll_protocol = htons(ETH_P_IP); 490 | socket_address.sll_ifindex = interface->ifindex; 491 | socket_address.sll_hatype = ARPHRD_ETHER; 492 | socket_address.sll_pkttype = PACKET_OTHERHOST; 493 | socket_address.sll_halen = ETH_ALEN; 494 | 495 | memcpy(socket_address.sll_addr, eh->h_source, ETH_ALEN); 496 | socket_address.sll_addr[6] = 0x00; /*not used*/ 497 | socket_address.sll_addr[7] = 0x00; /*not used*/ 498 | #endif 499 | 500 | /* Init IP Header */ 501 | #if defined(__FreeBSD__) || defined(__APPLE__) 502 | ip->ip_v = 4; 503 | ip->ip_hl = 5; 504 | ip->ip_tos = 0x10; 505 | ip->ip_len = htons(datalen + 8 + 20); 506 | ip->ip_id = htons(id++); 507 | ip->ip_off = htons(0x4000); 508 | ip->ip_ttl = 64; 509 | ip->ip_p = 17; /* UDP */ 510 | ip->ip_sum = 0; 511 | ip->ip_src.s_addr = sourceip->s_addr; 512 | ip->ip_dst.s_addr = destip->s_addr; 513 | #else 514 | ip->version = 4; 515 | ip->ihl = 5; 516 | ip->tos = 0x10; 517 | ip->tot_len = htons(datalen + 8 + 20); 518 | ip->id = htons(id++); 519 | ip->frag_off = htons(0x4000); 520 | ip->ttl = 64; 521 | ip->protocol = 17; /* UDP */ 522 | ip->check = 0x0000; 523 | ip->saddr = sourceip->s_addr; 524 | ip->daddr = destip->s_addr; 525 | #endif 526 | 527 | /* Calculate checksum for IP header */ 528 | #if defined(__FreeBSD__) || defined(__APPLE__) 529 | ip->ip_sum = in_cksum((unsigned short *)ip, sizeof(struct ip)); 530 | #else 531 | ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); 532 | #endif 533 | 534 | /* Init UDP Header */ 535 | #if defined(__FreeBSD__) || defined(__APPLE__) 536 | udp->uh_sport = htons(sourceport); 537 | udp->uh_dport = htons(destport); 538 | udp->uh_ulen = htons(sizeof(struct udphdr) + datalen); 539 | udp->uh_sum = 0; 540 | #else 541 | udp->source = htons(sourceport); 542 | udp->dest = htons(destport); 543 | udp->len = htons(sizeof(struct udphdr) + datalen); 544 | udp->check = 0; 545 | #endif 546 | 547 | /* Insert actual data */ 548 | memcpy(rest, data, datalen); 549 | 550 | /* Add UDP checksum */ 551 | #if defined(__FreeBSD__) || defined(__APPLE__) 552 | udp->uh_sum = udp_sum_calc((unsigned char *)&(ip->ip_src.s_addr), (unsigned char *)&(ip->ip_dst.s_addr), 553 | (unsigned char *)udp, sizeof(struct udphdr) + datalen); 554 | udp->uh_sum = htons(udp->uh_sum); 555 | #else 556 | udp->check = udp_sum_calc((unsigned char *)&(ip->saddr), (unsigned char *)&(ip->daddr), (unsigned char *)udp, 557 | sizeof(struct udphdr) + datalen); 558 | udp->check = htons(udp->check); 559 | #endif 560 | 561 | #ifdef __linux__ 562 | /* Send the packet */ 563 | send_result = 564 | sendto(fd, buffer, datalen + 8 + 14 + 20, 0, (struct sockaddr *)&socket_address, sizeof(socket_address)); 565 | if (send_result == -1) 566 | perror("sendto"); 567 | #else 568 | { 569 | struct ifreq req_if; 570 | 571 | /* Pick device to send through */ 572 | strcpy(req_if.ifr_name, interface->name); 573 | if (ioctl(fd, BIOCSETIF, &req_if) > 0) { 574 | perror("ioctl_BIOCSETIF"); 575 | exit(1); 576 | } 577 | } 578 | 579 | send_result = write(fd, buffer, datalen + 8 + 14 + 20); 580 | if (send_result == -1) { 581 | // perror("bpf_write"); 582 | return 0; 583 | } 584 | #endif 585 | 586 | /* Return amount of _data_ bytes sent */ 587 | if (send_result - 8 - 14 - 20 < 0) { 588 | return 0; 589 | } 590 | 591 | return send_result - 8 - 14 - 20; 592 | } 593 | -------------------------------------------------------------------------------- /po/Makefile.in.in: -------------------------------------------------------------------------------- 1 | # Makefile for PO directory in any package using GNU gettext. 2 | # Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper 3 | # 4 | # This file can be copied and used freely without restrictions. It can 5 | # be used in projects which are not available under the GNU General Public 6 | # License but which still want to provide support for the GNU gettext 7 | # functionality. 8 | # Please note that the actual code of GNU gettext is covered by the GNU 9 | # General Public License and is *not* in the public domain. 10 | # 11 | # Origin: gettext-0.19 12 | GETTEXT_MACRO_VERSION = 0.19 13 | 14 | PACKAGE = @PACKAGE@ 15 | VERSION = @VERSION@ 16 | PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ 17 | 18 | SED = @SED@ 19 | SHELL = /bin/sh 20 | @SET_MAKE@ 21 | 22 | srcdir = @srcdir@ 23 | top_srcdir = @top_srcdir@ 24 | VPATH = @srcdir@ 25 | 26 | prefix = @prefix@ 27 | exec_prefix = @exec_prefix@ 28 | datarootdir = @datarootdir@ 29 | datadir = @datadir@ 30 | localedir = @localedir@ 31 | gettextsrcdir = $(datadir)/gettext/po 32 | 33 | INSTALL = @INSTALL@ 34 | INSTALL_DATA = @INSTALL_DATA@ 35 | 36 | # We use $(mkdir_p). 37 | # In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as 38 | # "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, 39 | # @install_sh@ does not start with $(SHELL), so we add it. 40 | # In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined 41 | # either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake 42 | # versions, $(mkinstalldirs) and $(install_sh) are unused. 43 | mkinstalldirs = $(SHELL) @install_sh@ -d 44 | install_sh = $(SHELL) @install_sh@ 45 | MKDIR_P = @MKDIR_P@ 46 | mkdir_p = @mkdir_p@ 47 | 48 | GMSGFMT_ = @GMSGFMT@ 49 | GMSGFMT_no = @GMSGFMT@ 50 | GMSGFMT_yes = @GMSGFMT_015@ 51 | GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) 52 | MSGFMT_ = @MSGFMT@ 53 | MSGFMT_no = @MSGFMT@ 54 | MSGFMT_yes = @MSGFMT_015@ 55 | MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) 56 | XGETTEXT_ = @XGETTEXT@ 57 | XGETTEXT_no = @XGETTEXT@ 58 | XGETTEXT_yes = @XGETTEXT_015@ 59 | XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) 60 | MSGMERGE = msgmerge 61 | MSGMERGE_UPDATE = @MSGMERGE@ --update 62 | MSGINIT = msginit 63 | MSGCONV = msgconv 64 | MSGFILTER = msgfilter 65 | 66 | POFILES = @POFILES@ 67 | GMOFILES = @GMOFILES@ 68 | UPDATEPOFILES = @UPDATEPOFILES@ 69 | DUMMYPOFILES = @DUMMYPOFILES@ 70 | DISTFILES.common = Makefile.in.in remove-potcdate.sin \ 71 | $(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) 72 | DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ 73 | $(POFILES) $(GMOFILES) \ 74 | $(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) 75 | 76 | POTFILES = \ 77 | 78 | CATALOGS = @CATALOGS@ 79 | 80 | POFILESDEPS_ = $(srcdir)/$(DOMAIN).pot 81 | POFILESDEPS_yes = $(POFILESDEPS_) 82 | POFILESDEPS_no = 83 | POFILESDEPS = $(POFILESDEPS_$(PO_DEPENDS_ON_POT)) 84 | 85 | DISTFILESDEPS_ = update-po 86 | DISTFILESDEPS_yes = $(DISTFILESDEPS_) 87 | DISTFILESDEPS_no = 88 | DISTFILESDEPS = $(DISTFILESDEPS_$(DIST_DEPENDS_ON_UPDATE_PO)) 89 | 90 | # Makevars gets inserted here. (Don't remove this line!) 91 | 92 | .SUFFIXES: 93 | .SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update 94 | 95 | .po.mo: 96 | @echo "$(MSGFMT) -c -o $@ $<"; \ 97 | $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ 98 | 99 | .po.gmo: 100 | @lang=`echo $* | sed -e 's,.*/,,'`; \ 101 | test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ 102 | echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o $${lang}.gmo $${lang}.po"; \ 103 | cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo 104 | 105 | .sin.sed: 106 | sed -e '/^#/d' $< > t-$@ 107 | mv t-$@ $@ 108 | 109 | 110 | all: all-@USE_NLS@ 111 | 112 | all-yes: stamp-po 113 | all-no: 114 | 115 | # Ensure that the gettext macros and this Makefile.in.in are in sync. 116 | CHECK_MACRO_VERSION = \ 117 | test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \ 118 | || { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \ 119 | exit 1; \ 120 | } 121 | 122 | # $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no 123 | # internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because 124 | # we don't want to bother translators with empty POT files). We assume that 125 | # LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. 126 | # In this case, stamp-po is a nop (i.e. a phony target). 127 | 128 | # stamp-po is a timestamp denoting the last time at which the CATALOGS have 129 | # been loosely updated. Its purpose is that when a developer or translator 130 | # checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, 131 | # "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent 132 | # invocations of "make" will do nothing. This timestamp would not be necessary 133 | # if updating the $(CATALOGS) would always touch them; however, the rule for 134 | # $(POFILES) has been designed to not touch files that don't need to be 135 | # changed. 136 | stamp-po: $(srcdir)/$(DOMAIN).pot 137 | @$(CHECK_MACRO_VERSION) 138 | test ! -f $(srcdir)/$(DOMAIN).pot || \ 139 | test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) 140 | @test ! -f $(srcdir)/$(DOMAIN).pot || { \ 141 | echo "touch stamp-po" && \ 142 | echo timestamp > stamp-poT && \ 143 | mv stamp-poT stamp-po; \ 144 | } 145 | 146 | # Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', 147 | # otherwise packages like GCC can not be built if only parts of the source 148 | # have been downloaded. 149 | 150 | # This target rebuilds $(DOMAIN).pot; it is an expensive operation. 151 | # Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. 152 | # The determination of whether the package xyz is a GNU one is based on the 153 | # heuristic whether some file in the top level directory mentions "GNU xyz". 154 | # If GNU 'find' is available, we avoid grepping through monster files. 155 | $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed 156 | package_gnu="$(PACKAGE_GNU)"; \ 157 | test -n "$$package_gnu" || { \ 158 | if { if (LC_ALL=C find --version) 2>/dev/null | grep GNU >/dev/null; then \ 159 | LC_ALL=C find -L $(top_srcdir) -maxdepth 1 -type f \ 160 | -size -10000000c -exec grep 'GNU @PACKAGE@' \ 161 | /dev/null '{}' ';' 2>/dev/null; \ 162 | else \ 163 | LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null; \ 164 | fi; \ 165 | } | grep -v 'libtool:' >/dev/null; then \ 166 | package_gnu=yes; \ 167 | else \ 168 | package_gnu=no; \ 169 | fi; \ 170 | }; \ 171 | if test "$$package_gnu" = "yes"; then \ 172 | package_prefix='GNU '; \ 173 | else \ 174 | package_prefix=''; \ 175 | fi; \ 176 | if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ 177 | msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ 178 | else \ 179 | msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ 180 | fi; \ 181 | case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ 182 | '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \ 183 | $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ 184 | --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ 185 | --files-from=$(srcdir)/POTFILES.in \ 186 | --copyright-holder='$(COPYRIGHT_HOLDER)' \ 187 | --msgid-bugs-address="$$msgid_bugs_address" \ 188 | ;; \ 189 | *) \ 190 | $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ 191 | --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ 192 | --files-from=$(srcdir)/POTFILES.in \ 193 | --copyright-holder='$(COPYRIGHT_HOLDER)' \ 194 | --package-name="$${package_prefix}@PACKAGE@" \ 195 | --package-version='@VERSION@' \ 196 | --msgid-bugs-address="$$msgid_bugs_address" \ 197 | ;; \ 198 | esac 199 | test ! -f $(DOMAIN).po || { \ 200 | if test -f $(srcdir)/$(DOMAIN).pot; then \ 201 | sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ 202 | sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ 203 | if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ 204 | rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ 205 | else \ 206 | rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ 207 | mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ 208 | fi; \ 209 | else \ 210 | mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ 211 | fi; \ 212 | } 213 | 214 | # This rule has no dependencies: we don't need to update $(DOMAIN).pot at 215 | # every "make" invocation, only create it when it is missing. 216 | # Only "make $(DOMAIN).pot-update" or "make dist" will force an update. 217 | $(srcdir)/$(DOMAIN).pot: 218 | $(MAKE) $(DOMAIN).pot-update 219 | 220 | # This target rebuilds a PO file if $(DOMAIN).pot has changed. 221 | # Note that a PO file is not touched if it doesn't need to be changed. 222 | $(POFILES): $(POFILESDEPS) 223 | @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ 224 | if test -f "$(srcdir)/$${lang}.po"; then \ 225 | test -f $(srcdir)/$(DOMAIN).pot || $(MAKE) $(srcdir)/$(DOMAIN).pot; \ 226 | test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ 227 | echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \ 228 | cd $(srcdir) \ 229 | && { case `$(MSGMERGE_UPDATE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ 230 | '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ 231 | $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \ 232 | *) \ 233 | $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot;; \ 234 | esac; \ 235 | }; \ 236 | else \ 237 | $(MAKE) $${lang}.po-create; \ 238 | fi 239 | 240 | 241 | install: install-exec install-data 242 | install-exec: 243 | install-data: install-data-@USE_NLS@ 244 | if test "$(PACKAGE)" = "gettext-tools"; then \ 245 | $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ 246 | for file in $(DISTFILES.common) Makevars.template; do \ 247 | $(INSTALL_DATA) $(srcdir)/$$file \ 248 | $(DESTDIR)$(gettextsrcdir)/$$file; \ 249 | done; \ 250 | for file in Makevars; do \ 251 | rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ 252 | done; \ 253 | else \ 254 | : ; \ 255 | fi 256 | install-data-no: all 257 | install-data-yes: all 258 | @catalogs='$(CATALOGS)'; \ 259 | for cat in $$catalogs; do \ 260 | cat=`basename $$cat`; \ 261 | lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ 262 | dir=$(localedir)/$$lang/LC_MESSAGES; \ 263 | $(mkdir_p) $(DESTDIR)$$dir; \ 264 | if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ 265 | $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ 266 | echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ 267 | for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ 268 | if test -n "$$lc"; then \ 269 | if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ 270 | link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ 271 | mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ 272 | mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ 273 | (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ 274 | for file in *; do \ 275 | if test -f $$file; then \ 276 | ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ 277 | fi; \ 278 | done); \ 279 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ 280 | else \ 281 | if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ 282 | :; \ 283 | else \ 284 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ 285 | mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ 286 | fi; \ 287 | fi; \ 288 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ 289 | ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ 290 | ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ 291 | cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ 292 | echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ 293 | fi; \ 294 | done; \ 295 | done 296 | 297 | install-strip: install 298 | 299 | installdirs: installdirs-exec installdirs-data 300 | installdirs-exec: 301 | installdirs-data: installdirs-data-@USE_NLS@ 302 | if test "$(PACKAGE)" = "gettext-tools"; then \ 303 | $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ 304 | else \ 305 | : ; \ 306 | fi 307 | installdirs-data-no: 308 | installdirs-data-yes: 309 | @catalogs='$(CATALOGS)'; \ 310 | for cat in $$catalogs; do \ 311 | cat=`basename $$cat`; \ 312 | lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ 313 | dir=$(localedir)/$$lang/LC_MESSAGES; \ 314 | $(mkdir_p) $(DESTDIR)$$dir; \ 315 | for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ 316 | if test -n "$$lc"; then \ 317 | if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ 318 | link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ 319 | mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ 320 | mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ 321 | (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ 322 | for file in *; do \ 323 | if test -f $$file; then \ 324 | ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ 325 | fi; \ 326 | done); \ 327 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ 328 | else \ 329 | if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ 330 | :; \ 331 | else \ 332 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ 333 | mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ 334 | fi; \ 335 | fi; \ 336 | fi; \ 337 | done; \ 338 | done 339 | 340 | # Define this as empty until I found a useful application. 341 | installcheck: 342 | 343 | uninstall: uninstall-exec uninstall-data 344 | uninstall-exec: 345 | uninstall-data: uninstall-data-@USE_NLS@ 346 | if test "$(PACKAGE)" = "gettext-tools"; then \ 347 | for file in $(DISTFILES.common) Makevars.template; do \ 348 | rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ 349 | done; \ 350 | else \ 351 | : ; \ 352 | fi 353 | uninstall-data-no: 354 | uninstall-data-yes: 355 | catalogs='$(CATALOGS)'; \ 356 | for cat in $$catalogs; do \ 357 | cat=`basename $$cat`; \ 358 | lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ 359 | for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ 360 | rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ 361 | done; \ 362 | done 363 | 364 | check: all 365 | 366 | info dvi ps pdf html tags TAGS ctags CTAGS ID: 367 | 368 | mostlyclean: 369 | rm -f remove-potcdate.sed 370 | rm -f stamp-poT 371 | rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po 372 | rm -fr *.o 373 | 374 | clean: mostlyclean 375 | 376 | distclean: clean 377 | rm -f Makefile Makefile.in POTFILES *.mo 378 | 379 | maintainer-clean: distclean 380 | @echo "This command is intended for maintainers to use;" 381 | @echo "it deletes files that may require special tools to rebuild." 382 | rm -f stamp-po $(GMOFILES) 383 | 384 | distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) 385 | dist distdir: 386 | test -z "$(DISTFILESDEPS)" || $(MAKE) $(DISTFILESDEPS) 387 | @$(MAKE) dist2 388 | # This is a separate target because 'update-po' must be executed before. 389 | dist2: stamp-po $(DISTFILES) 390 | dists="$(DISTFILES)"; \ 391 | if test "$(PACKAGE)" = "gettext-tools"; then \ 392 | dists="$$dists Makevars.template"; \ 393 | fi; \ 394 | if test -f $(srcdir)/$(DOMAIN).pot; then \ 395 | dists="$$dists $(DOMAIN).pot stamp-po"; \ 396 | fi; \ 397 | if test -f $(srcdir)/ChangeLog; then \ 398 | dists="$$dists ChangeLog"; \ 399 | fi; \ 400 | for i in 0 1 2 3 4 5 6 7 8 9; do \ 401 | if test -f $(srcdir)/ChangeLog.$$i; then \ 402 | dists="$$dists ChangeLog.$$i"; \ 403 | fi; \ 404 | done; \ 405 | if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ 406 | for file in $$dists; do \ 407 | if test -f $$file; then \ 408 | cp -p $$file $(distdir) || exit 1; \ 409 | else \ 410 | cp -p $(srcdir)/$$file $(distdir) || exit 1; \ 411 | fi; \ 412 | done 413 | 414 | update-po: Makefile 415 | $(MAKE) $(DOMAIN).pot-update 416 | test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) 417 | $(MAKE) update-gmo 418 | 419 | # General rule for creating PO files. 420 | 421 | .nop.po-create: 422 | @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ 423 | echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ 424 | exit 1 425 | 426 | # General rule for updating PO files. 427 | 428 | .nop.po-update: 429 | @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ 430 | if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \ 431 | tmpdir=`pwd`; \ 432 | echo "$$lang:"; \ 433 | test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ 434 | echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ 435 | cd $(srcdir); \ 436 | if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ 437 | '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ 438 | $(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ 439 | *) \ 440 | $(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ 441 | esac; \ 442 | }; then \ 443 | if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ 444 | rm -f $$tmpdir/$$lang.new.po; \ 445 | else \ 446 | if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ 447 | :; \ 448 | else \ 449 | echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ 450 | exit 1; \ 451 | fi; \ 452 | fi; \ 453 | else \ 454 | echo "msgmerge for $$lang.po failed!" 1>&2; \ 455 | rm -f $$tmpdir/$$lang.new.po; \ 456 | fi 457 | 458 | $(DUMMYPOFILES): 459 | 460 | update-gmo: Makefile $(GMOFILES) 461 | @: 462 | 463 | # Recreate Makefile by invoking config.status. Explicitly invoke the shell, 464 | # because execution permission bits may not work on the current file system. 465 | # Use @SHELL@, which is the shell determined by autoconf for the use by its 466 | # scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient. 467 | Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ 468 | cd $(top_builddir) \ 469 | && @SHELL@ ./config.status $(subdir)/$@.in po-directories 470 | 471 | force: 472 | 473 | # Tell versions [3.59,3.63) of GNU make not to export all variables. 474 | # Otherwise a system limit (for SysV at least) may be exceeded. 475 | .NOEXPORT: 476 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /po/nb.po: -------------------------------------------------------------------------------- 1 | # Norsk 2 | # Copyright (C) 2013 Håkon Nessjøen 3 | # This file is distributed under the same license as the mactelnet package. 4 | # Håkon Nessjøen , 2016 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: mactelnet\n" 9 | "Report-Msgid-Bugs-To: haakon.nessjoen@gmail.com\n" 10 | "POT-Creation-Date: 2024-08-12 14:00+0200\n" 11 | "PO-Revision-Date: 2024-08-10 13:42+0200\n" 12 | "Last-Translator: Håkon Nessjøen \n" 13 | "Language-Team: Håkon Nessjøen \n" 14 | "Language: nb\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "X-Generator: Poedit 3.4.4\n" 19 | "X-Poedit-SourceCharset: UTF-8\n" 20 | 21 | #: src/mactelnet.c:138 22 | #, c-format 23 | msgid "" 24 | "Failed dropping privileges. The user %s is not a valid username on local " 25 | "system.\n" 26 | msgstr "" 27 | "Klarte ikke slippe tilgangsnivå. Brukernavnet %s er ikke gyldig på det " 28 | "lokale systemet.\n" 29 | 30 | #: src/mactelnet.c:145 31 | #, c-format 32 | msgid "setgid: Error dropping group privileges\n" 33 | msgstr "setgid: Klarte ikke slippe tilgangsnivå for gruppe\n" 34 | 35 | #: src/mactelnet.c:149 36 | #, c-format 37 | msgid "setuid: Error dropping user privileges\n" 38 | msgstr "setud: Klarte ikke slippe tilgangsnivå for bruker\n" 39 | 40 | #: src/mactelnet.c:154 41 | #, c-format 42 | msgid "Failed to drop privileges\n" 43 | msgstr "Klarte ikke slippe tilgangsnivå\n" 44 | 45 | #: src/mactelnet.c:222 46 | #, c-format 47 | msgid "" 48 | "\n" 49 | "Connection timed out\n" 50 | msgstr "" 51 | "\n" 52 | "Tilkoblingen fikk tidsavbrudd\n" 53 | 54 | #: src/mactelnet.c:258 55 | #, c-format 56 | msgid "Error: md5 digest not found\n" 57 | msgstr "Feil: md5 funksjon ikke funet\n" 58 | 59 | #: src/mactelnet.c:263 60 | #, c-format 61 | msgid "Error initializing md5 context\n" 62 | msgstr "Klarte ikke initialisere md5 kontekst\n" 63 | 64 | #: src/mactelnet.c:379 65 | #, c-format 66 | msgid "Invalid salt length: %d (instead of 16 or 49) received from server %s\n" 67 | msgstr "" 68 | "Ugyldig saltlengde: %d (i stedet for 16 eller 49) mottatt fra serveren %s\n" 69 | 70 | #: src/mactelnet.c:426 71 | #, c-format 72 | msgid "Connection closed.\n" 73 | msgstr "Tilkoblingen ble lukket.\n" 74 | 75 | #: src/mactelnet.c:432 76 | #, c-format 77 | msgid "Unhandeled packet type: %d received from server %s\n" 78 | msgstr "Uhåndtert pakketype: %d mottatt fra server %s\n" 79 | 80 | #: src/mactelnet.c:453 81 | #, c-format 82 | msgid "Error: No suitable devices found\n" 83 | msgstr "Feil: Ingen brukbare enheter funnet\n" 84 | 85 | #: src/mactelnet.c:617 86 | #, c-format 87 | msgid "" 88 | "Usage: %s [-nqoA] [-a ] [-t ] [-u ] [-p " 89 | "] [-U ] | -l [-B] [-t ]\n" 90 | msgstr "" 91 | "Bruk: %s [-nqoA] [-a ] [-t ] [-u ] " 92 | "[-p ] [-U ] | -l [-B] [-t ]\n" 93 | 94 | #: src/mactelnet.c:623 95 | #, c-format 96 | msgid "" 97 | "\n" 98 | "Parameters:\n" 99 | " MAC MAC-Address of the RouterOS/mactelnetd device. Use mndp to\n" 100 | " discover it.\n" 101 | " identity The identity/name of your destination device. Uses\n" 102 | " MNDP protocol to find it.\n" 103 | " -l List/Search for routers nearby (MNDP). You may use -t to " 104 | "set timeout.\n" 105 | " -B Batch mode. Use computer readable output (CSV), for use " 106 | "with -l.\n" 107 | " -n Do not use broadcast packets. Less insecure but requires\n" 108 | " root privileges.\n" 109 | " -a Use specified path instead of the default: %s for autologin " 110 | "config file.\n" 111 | " -A Disable autologin feature.\n" 112 | " -t Amount of seconds to wait for a successufl connection, or " 113 | "amount of seconds\n" 114 | " to wait for MNDP response (when used with -l).\n" 115 | " -u Specify username on command line.\n" 116 | " -p Specify password on command line.\n" 117 | " -U Drop privileges to this user. Used in conjunction with -n\n" 118 | " for security.\n" 119 | " -q Quiet mode.\n" 120 | " -o Force old MD5 authentication algorithm.\n" 121 | " -h This help.\n" 122 | "\n" 123 | msgstr "" 124 | "\n" 125 | "Parametere:\n" 126 | " MAC MAC-Adressen til RouterOS/mactelnetd enheten. Bruk mndb " 127 | "for\n" 128 | " å finne den.\n" 129 | " identitet Identiteten til enheten. Bruker MNDP protokollen\n" 130 | " til å finne den.\n" 131 | " -l List/Søk etter routere i nærheten (bruker MNDP). Du kan \n" 132 | " bruke -t til å sette timeout.\n" 133 | " -B Batch modus. Gir datamaskin-lesbar info for data fra -l.\n" 134 | " -n Ikke bruk broadcast pakker. Mindre usikkert, men bruker\n" 135 | " root privilegier.\n" 136 | " -a Bruk denne mappen isteden for: %s for autologin " 137 | "konfigurasjonsfil.\n" 138 | " -A Skru av atuologin funksjonalitet.\n" 139 | " -t Antall sekunder å vente for en vellykket tilkobling, eller\n" 140 | " antall sekunder å vente på MNDP svar.\n" 141 | " -u Spesifiser brukernavn på kommando-linjen.\n" 142 | " -p Spesifiser passord på kommando-linjen.\n" 143 | " -U Dropp rettigheter ned til denne brukeren. Brukes i " 144 | "forbindelse\n" 145 | " med -n for sikkerhet.\n" 146 | " -q Stille-modus.\n" 147 | " -o Tving bruk av gammel MD5-autentiseringsalgoritme.\n" 148 | " -h Denne hjelpen.\n" 149 | "\n" 150 | 151 | #: src/mactelnet.c:659 152 | #, c-format 153 | msgid "Using autologin credentials from %s\n" 154 | msgstr "Bruker autologin-påloggingsinformasjon fra %s\n" 155 | 156 | #: src/mactelnet.c:683 157 | #, c-format 158 | msgid "You need to have root privileges to use the -n parameter.\n" 159 | msgstr "Du trenger root privilegier for å bruke -n parameteren.\n" 160 | 161 | #: src/mactelnet.c:693 162 | #, c-format 163 | msgid "The -U option must be used in conjunction with the -n parameter.\n" 164 | msgstr "-U parameteren må brukes i sammenheng med -n parameteren.\n" 165 | 166 | #: src/mactelnet.c:722 167 | #, c-format 168 | msgid "Login: " 169 | msgstr "Bruker: " 170 | 171 | #: src/mactelnet.c:731 src/mactelnet.c:733 src/mactelnetd.c:1103 172 | #: src/mactelnetd.c:1105 173 | msgid "Password: " 174 | msgstr "Passord: " 175 | 176 | #: src/mactelnet.c:765 177 | #, c-format 178 | msgid "Connecting to %s..." 179 | msgstr "Kobler til %s…" 180 | 181 | #: src/mactelnet.c:775 src/mactelnetd.c:268 src/mactelnetd.c:1335 182 | #, c-format 183 | msgid "Error binding to %s:%d, %s\n" 184 | msgstr "Problemer med å binde til %s:%d, %s\n" 185 | 186 | #: src/mactelnet.c:780 187 | #, c-format 188 | msgid "Connection failed.\n" 189 | msgstr "Tilkobling feilet.\n" 190 | 191 | #: src/mactelnet.c:784 192 | #, c-format 193 | msgid "done\n" 194 | msgstr "ferdig\n" 195 | 196 | #: src/mactelnet.c:795 197 | #, c-format 198 | msgid "Username too long\n" 199 | msgstr "Brukernavn for langt\n" 200 | 201 | #: src/mactelnetd.c:270 202 | #, c-format 203 | msgid "Error binding to %s:%d on %s\n" 204 | msgstr "Problemer med å binde til %s:%d på %s\n" 205 | 206 | #: src/mactelnetd.c:274 207 | #, c-format 208 | msgid "Using %s to transmit packets from %s\n" 209 | msgstr "Bruker %s til å sende pakker fra %s\n" 210 | 211 | #: src/mactelnetd.c:473 212 | #, c-format 213 | msgid "" 214 | "(%d) User %s tried to login with md5 authentication, but user is not saved " 215 | "in plaintext" 216 | msgstr "" 217 | "(%d) Brukeren %s forsøkte å logge inn med md5 autentisering, men brukerens " 218 | "passord er ikke lagret i klartekst" 219 | 220 | #: src/mactelnetd.c:493 221 | #, c-format 222 | msgid "(%d) Invalid login by %s." 223 | msgstr "(%d) Ugyldig login av %s." 224 | 225 | #: src/mactelnetd.c:496 226 | msgid "Login failed, incorrect username or password\r\n" 227 | msgstr "Login feilet, ugyldig brukernavn eller passord\r\n" 228 | 229 | #: src/mactelnetd.c:513 230 | msgid "Terminal error\r\n" 231 | msgstr "Terminalfeil\r\n" 232 | 233 | #: src/mactelnetd.c:529 234 | #, c-format 235 | msgid "(%d) Error allocating memory." 236 | msgstr "(%d) Klarer ikke allokere minne." 237 | 238 | #: src/mactelnetd.c:531 239 | msgid "System error, out of memory\r\n" 240 | msgstr "Systemfeil, minne fullt\r\n" 241 | 242 | #: src/mactelnetd.c:537 243 | #, c-format 244 | msgid "(%d) Login ok, but local user not accessible (%s)." 245 | msgstr "(%d) Login ok, men lokal bruker er ikke tilgjengelig (%s)." 246 | 247 | #: src/mactelnetd.c:540 248 | msgid "Error: Local user not accessible\r\n" 249 | msgstr "Lokal bruker er ikke tilgjengelig\r\n" 250 | 251 | #: src/mactelnetd.c:550 252 | #, c-format 253 | msgid "Error opening %s: %s" 254 | msgstr "Klarer ikke åpne %s: %s" 255 | 256 | #: src/mactelnetd.c:553 257 | msgid "Error opening terminal\r\n" 258 | msgstr "Klarer ikke åpne terminal\r\n" 259 | 260 | #: src/mactelnetd.c:564 261 | #, c-format 262 | msgid "(%d) User %s logged in." 263 | msgstr "(%d) Bruker %s logget inn." 264 | 265 | #: src/mactelnetd.c:599 266 | #, c-format 267 | msgid "(%d) Could not log in %s (%d:%d): setuid/setgid: %s" 268 | msgstr "(%d) Kunne ikke logge inn %s (%d:%d): setuid/setgid: %s" 269 | 270 | #: src/mactelnetd.c:602 271 | msgid "Internal error\r\n" 272 | msgstr "Intern feil\r\n" 273 | 274 | #: src/mactelnetd.c:608 275 | #, c-format 276 | msgid "(%d) User %s disconnected with " 277 | msgstr "(%d) Bruker %s frakoblet med " 278 | 279 | #: src/mactelnetd.c:752 280 | #, c-format 281 | msgid "(%d) Invalid mtwei key by %s." 282 | msgstr "(%d) Ugyldig mtwei nøkkel av %s." 283 | 284 | #: src/mactelnetd.c:775 285 | #, c-format 286 | msgid "(%d) Unhandeled control packet type: %d, length: %d" 287 | msgstr "(%d) Uhåndtert kontrollpakke-type: %d, lengde: %d" 288 | 289 | #: src/mactelnetd.c:779 290 | #, c-format 291 | msgid "(%d) Unhandeled control packet type: %d, in state: %d, length: %d" 292 | msgstr "(%d) Uhåndtert kontrollpakke-type: %d, i tilstand: %d, lengde: %d" 293 | 294 | #: src/mactelnetd.c:835 295 | #, c-format 296 | msgid "(%d) New connection from %s." 297 | msgstr "(%d) Ny tilkobling fra %s." 298 | 299 | #: src/mactelnetd.c:864 src/mactelnetd.c:1490 300 | #, c-format 301 | msgid "(%d) Connection closed." 302 | msgstr "(%d) Tilkobling lukket." 303 | 304 | #: src/mactelnetd.c:919 305 | #, c-format 306 | msgid "(%d) Unhandeled packet type: %d" 307 | msgstr "(%d) Uhåndtert pakke-type: %d" 308 | 309 | #: src/mactelnetd.c:994 310 | msgid "Was not able to send any MNDP packets" 311 | msgstr "Klarte ikke sende MNDP pakker" 312 | 313 | #: src/mactelnetd.c:1003 314 | msgid "" 315 | "\r\n" 316 | "\r\n" 317 | "Daemon shutting down.\r\n" 318 | msgstr "" 319 | "\r\n" 320 | "\r\n" 321 | "Tjener avslutter.\r\n" 322 | 323 | #: src/mactelnetd.c:1005 324 | msgid "Daemon shutting down" 325 | msgstr "Tjener avslutter" 326 | 327 | #: src/mactelnetd.c:1038 328 | msgid "SIGHUP: Reloading interfaces" 329 | msgstr "SIGHUP: Laster grensesnitt på nytt" 330 | 331 | #: src/mactelnetd.c:1053 332 | msgid "No devices found! Exiting.\n" 333 | msgstr "Ingen enheter funnet! Avslutter.\n" 334 | 335 | #: src/mactelnetd.c:1067 336 | #, c-format 337 | msgid "(%d) Connection closed because interface %s is gone." 338 | msgstr "(%d) Tilkobling lukket på grunn av at %s er borte." 339 | 340 | #: src/mactelnetd.c:1080 341 | #, c-format 342 | msgid "Username: " 343 | msgstr "Brukernavn: " 344 | 345 | #: src/mactelnetd.c:1085 src/mactelnetd.c:1128 346 | #, c-format 347 | msgid "Username must be specified.\n" 348 | msgstr "Brukernavn må spesifiseres.\n" 349 | 350 | #: src/mactelnetd.c:1092 351 | #, c-format 352 | msgid "Username too long.\n" 353 | msgstr "Brukernavnet er for langt\n" 354 | 355 | #: src/mactelnetd.c:1098 356 | #, c-format 357 | msgid "Warning: Local user '%s' does not exist.\n" 358 | msgstr "Advarsel: Lokal bruker '%s' finnes ikke.\n" 359 | 360 | #: src/mactelnetd.c:1108 361 | #, c-format 362 | msgid "Password must be specified.\n" 363 | msgstr "Passord må spesifiseres.\n" 364 | 365 | #: src/mactelnetd.c:1116 366 | #, c-format 367 | msgid "User %s was added.\n" 368 | msgstr "Bruker %s ble lagt til.\n" 369 | 370 | #: src/mactelnetd.c:1118 371 | #, c-format 372 | msgid "User %s was updated.\n" 373 | msgstr "Bruker %s ble oppdatert.\n" 374 | 375 | #: src/mactelnetd.c:1120 376 | #, c-format 377 | msgid "Failed to add user %s.\n" 378 | msgstr "Klarte ikke legge til brukeren %s.\n" 379 | 380 | #: src/mactelnetd.c:1134 381 | #, c-format 382 | msgid "User %s was deleted.\n" 383 | msgstr "Brukeren %s ble slettet.\n" 384 | 385 | #: src/mactelnetd.c:1136 386 | #, c-format 387 | msgid "User %s did not exist.\n" 388 | msgstr "Brukeren %s fantes ikke.\n" 389 | 390 | #: src/mactelnetd.c:1138 391 | #, c-format 392 | msgid "Failed to delete user %s.\n" 393 | msgstr "Klarte ikke å slette bruker %s.\n" 394 | 395 | #: src/mactelnetd.c:1147 396 | #, c-format 397 | msgid "Users:\n" 398 | msgstr "Brukere:\n" 399 | 400 | #: src/mactelnetd.c:1152 401 | msgid "local user not found!" 402 | msgstr "lokal bruker ikke funnet!" 403 | 404 | #: src/mactelnetd.c:1154 405 | msgid "has root access!" 406 | msgstr "har root tilgang!" 407 | 408 | #: src/mactelnetd.c:1156 409 | msgid "plain-text password!" 410 | msgstr "klartekst-passord!" 411 | 412 | #: src/mactelnetd.c:1242 413 | #, c-format 414 | msgid "Usage: %s [-fnovh]|-l|-a [-u |-p ]|[-d ]\n" 415 | msgstr "Bruk: %s [-fnovh]|-l|-a [-u |-p ]|[-d ]\n" 416 | 417 | #: src/mactelnetd.c:1247 418 | #, c-format 419 | msgid "" 420 | "\n" 421 | "Parameters:\n" 422 | " -f Run process in foreground.\n" 423 | " -n Do not use broadcast packets. Just a tad less insecure.\n" 424 | " -o Use MD5 for password hashing. (less secure)\n" 425 | " -l List users from userfile.\n" 426 | " -a Add a new user.\n" 427 | " -u [user] Optionally set username to add with -a.\n" 428 | " -p [password] Optionally set password for -a command.\n" 429 | " -d [user] Delete user.\n" 430 | " -h This help.\n" 431 | "\n" 432 | "\n" 433 | "If any of -a, -d, -l or -h is specified, the server will not start.\n" 434 | "\n" 435 | msgstr "" 436 | "\n" 437 | "Parametere:\n" 438 | " -f Kjør prosessen i forgrunnen.\n" 439 | " -n Ikke bruk kringkastningsmeldinger. Såvidt mindre usikkert.\n" 440 | " -o Bruk MD5 for passord hashing. (mindre sikkert)\n" 441 | " -l List brukere fra brukerfilen.\n" 442 | " -a Legg til en bruker.\n" 443 | " -u [bruker] Angi brukernavn som skal legges til med -a. (valgfritt)\n" 444 | " -p [passord] Angi passord for -a-kommandoen. (valgfritt)\n" 445 | " -d [bruker] Slett bruker.\n" 446 | " -h Denne hjelpen.\n" 447 | "\n" 448 | "\n" 449 | "Hvis noen av parameterene -a, -d, -l eller -h er spesifisert, vil ikke " 450 | "serveren starte.\n" 451 | 452 | #: src/mactelnetd.c:1262 453 | #, c-format 454 | msgid "" 455 | "\n" 456 | "Parameters:\n" 457 | " -n Do not use broadcast packets. Just a tad less insecure.\n" 458 | " -o Use MD5 for password hashing. (less secure)\n" 459 | " -l List users from userfile.\n" 460 | " -a Add a new user.\n" 461 | " -u [user] Optionally set username to add with -a.\n" 462 | " -p [password] Optionally set password for -a command.\n" 463 | " -d [user] Delete user.\n" 464 | " -h This help.\n" 465 | "\n" 466 | "\n" 467 | "If any of -a, -d, -l or -h is specified, the server will not start.\n" 468 | "\n" 469 | msgstr "" 470 | "\n" 471 | "Parametere:\n" 472 | " -n Ikke bruk kringkastningsmeldinger. Såvidt mindre usikkert.\n" 473 | " -o Bruk MD5 for passord hashing. (mindre sikkert)\n" 474 | " -l List brukere fra brukerfilen.\n" 475 | " -a Legg til en bruker.\n" 476 | " -u [bruker] Angi brukernavn som skal legges til med -a. (valgfritt)\n" 477 | " -p [passord] Angi passord for -a-kommandoen. (valgfritt)\n" 478 | " -d [bruker] Slett bruker.\n" 479 | " -h Denne hjelpen.\n" 480 | "\n" 481 | "\n" 482 | "Hvis noen av parameterene -a, -d, -l eller -h er spesifisert, vil ikke " 483 | "serveren starte.\n" 484 | 485 | #: src/mactelnetd.c:1279 src/macping.c:189 486 | #, c-format 487 | msgid "You need to have root privileges to use %s.\n" 488 | msgstr "Du trenger superbruker-rettigheter for å bruke %s.\n" 489 | 490 | #: src/mactelnetd.c:1357 491 | #, c-format 492 | msgid "MNDP: Error binding to %s:%d, %s\n" 493 | msgstr "MNDP: Klarte ikke binde til %s:%d, %s\n" 494 | 495 | #: src/mactelnetd.c:1362 496 | #, c-format 497 | msgid "Bound to %s:%d" 498 | msgstr "Bundet til %s:%d" 499 | 500 | #: src/mactelnetd.c:1396 501 | msgid "Unable to find any valid network interfaces\n" 502 | msgstr "Klarte ikke finne noen gyldige nettverksgrensesnitt\n" 503 | 504 | #: src/mactelnetd.c:1441 src/interfaces.c:284 505 | msgid "Network change detected" 506 | msgstr "Nettverksforandring oppdaget" 507 | 508 | #: src/mactelnetd.c:1488 509 | #, c-format 510 | msgid "(%d) Connection to user %s closed." 511 | msgstr "(%d) Tilkobling til bruker %s lukket." 512 | 513 | #: src/mactelnetd.c:1496 514 | #, c-format 515 | msgid "(%d) Waiting for ack\n" 516 | msgstr "(%d) Venter på ack\n" 517 | 518 | #: src/mactelnetd.c:1512 519 | #, c-format 520 | msgid "(%d) Session timed out" 521 | msgstr "(%d) Sesjonen utgikk på tidsavbrudd" 522 | 523 | #: src/mactelnetd.c:1515 524 | msgid "Timeout\r\n" 525 | msgstr "Tidsavbrudd\r\n" 526 | 527 | #: src/macping.c:100 528 | #, c-format 529 | msgid "%d packets transmitted, %d packets received, %d%% packet loss\n" 530 | msgstr "%d pakker sendt, %d pakker mottatt, %d%% pakketap\n" 531 | 532 | #: src/macping.c:102 533 | #, c-format 534 | msgid "round-trip min/avg/max = %.2f/%.2f/%.2f ms\n" 535 | msgstr "returtid min/gjennomsnitt/maks = %.2f/%.2f/%.2f ms\n" 536 | 537 | #: src/macping.c:159 538 | #, c-format 539 | msgid "" 540 | "Number of packets to send must be more than 0 and up to 100 in fast mode.\n" 541 | msgstr "Antall pakker må være mer enn 0 og opp til 100 i hurtig-modus.\n" 542 | 543 | #: src/macping.c:165 544 | #, c-format 545 | msgid "Usage: %s [-h] [-f] [-c ] [-s ]\n" 546 | msgstr "Bruksmåte: %s [-h] [-f] [-c ] [-s ]\n" 547 | 548 | #: src/macping.c:168 549 | #, c-format 550 | msgid "" 551 | "\n" 552 | "Parameters:\n" 553 | " MAC MAC-Address of the RouterOS/mactelnetd device.\n" 554 | " -f Fast mode, do not wait before sending next ping request.\n" 555 | " -s Specify size of ping packet.\n" 556 | " -c Number of packets to send. (0 = unlimited)\n" 557 | " -h This help.\n" 558 | "\n" 559 | msgstr "" 560 | "\n" 561 | "Parametere:\n" 562 | " MAC MAC-Adressen til RouterOS/mactelnetd enheten.\n" 563 | " -f Hurtig-modus, send ping forespørsler uten pause.\n" 564 | " -s Spesifiser pakkestørrelsen.\n" 565 | " -c Antall pakker som skal sendes. (0 = uendelig)\n" 566 | " -h Denne hjelpen.\n" 567 | "\n" 568 | 569 | #: src/macping.c:180 570 | #, c-format 571 | msgid "Packet size must be between 18 and %d\n" 572 | msgstr "Pakkestørrelse må være mellom 18 og %d\n" 573 | 574 | #: src/macping.c:218 src/mndp.c:95 src/protocol.c:444 575 | #, c-format 576 | msgid "Error binding to %s:%d\n" 577 | msgstr "Klarte ikke binde mot %s:%d\n" 578 | 579 | #: src/macping.c:272 580 | #, c-format 581 | msgid "Error sending packet.\n" 582 | msgstr "Klarte ikke sende pakke.\n" 583 | 584 | #: src/macping.c:290 585 | #, c-format 586 | msgid "%s ping timeout\n" 587 | msgstr "%s ping tidsavbrudd\n" 588 | 589 | #: src/macping.c:332 590 | #, c-format 591 | msgid "%s %d byte, ping time %.2f ms%s\n" 592 | msgstr "%s %d byte, ping tid %.2f ms%s\n" 593 | 594 | #: src/macping.c:336 595 | #, c-format 596 | msgid "%s Reply of %d bytes of unequal data\n" 597 | msgstr "%s Retur av %d byter med usammensvarende data\n" 598 | 599 | #: src/mndp.c:100 600 | #, c-format 601 | msgid "Searching for MikroTik routers... Abort with CTRL+C.\n" 602 | msgstr "Søker etter MikroTik rutere… Avbryt med CTRL+C.\n" 603 | 604 | #: src/mndp.c:104 605 | #, c-format 606 | msgid "Unable to send broadcast packets: Operating in receive only mode.\n" 607 | msgstr "Klarer ikke sende broadcast pakker: Jobber i kun-motta-modus.\n" 608 | 609 | #: src/mndp.c:114 610 | #, c-format 611 | msgid "Unable to send broadcast packet: Operating in receive only mode.\n" 612 | msgstr "Klarer ikke sende broadcast pakke: Jobber i kun-motta-modus.\n" 613 | 614 | #: src/mndp.c:121 615 | msgid "IP" 616 | msgstr "IP" 617 | 618 | #: src/mndp.c:121 619 | msgid "MAC-Address" 620 | msgstr "MAC-Adresse" 621 | 622 | #: src/mndp.c:122 623 | msgid "Identity (platform version hardware) uptime" 624 | msgstr "Identitet (plattform versjon maskinvare) oppetid" 625 | 626 | #: src/mndp.c:141 627 | #, c-format 628 | msgid "An error occurred. aborting\n" 629 | msgstr "En feil oppstod. avbryter\n" 630 | 631 | #: src/mndp.c:156 632 | #, c-format 633 | msgid " up %d days %d hours" 634 | msgstr " oppe %d dager %d timer" 635 | 636 | #: src/autologin.c:87 637 | #, c-format 638 | msgid "Error opening autologin file %s: %s\n" 639 | msgstr "Klarer ikke åpne autologinfilen %s: %s\n" 640 | 641 | #: src/autologin.c:137 642 | #, c-format 643 | msgid "Error on line %d in %s: New line in middle of identifier\n" 644 | msgstr "Feil på linje %d i %s: Ny linje midt inni identifikatoren\n" 645 | 646 | #: src/autologin.c:145 647 | #, c-format 648 | msgid "Error on line %d in %s: Identifier string too long.\n" 649 | msgstr "Feil på linje %d i %s: Identifikatorstreng for lang.\n" 650 | 651 | #: src/autologin.c:169 652 | #, c-format 653 | msgid "Error on line %d in %s: Newline before '=' character\n" 654 | msgstr "Feil på linje %d i %s: Ny linje før '=' tegn\n" 655 | 656 | #: src/autologin.c:177 657 | #, c-format 658 | msgid "Error on line %d in %s: Key string too long.\n" 659 | msgstr "Feil på linje %d i %s: Nøkkelstreng for lang.\n" 660 | 661 | #: src/autologin.c:199 662 | #, c-format 663 | msgid "Warning on line %d of %s: Unknown parameter %s, ignoring.\n" 664 | msgstr "Advarsel på linje %d i %s: Ugyldig parameter %s, ignorerer.\n" 665 | 666 | #: src/autologin.c:211 667 | #, c-format 668 | msgid "Error on line %d in %s: Value string too long.\n" 669 | msgstr "Feil på linje %d i %s: Strengverdi for lang.\n" 670 | 671 | #: src/interfaces.c:86 672 | #, c-format 673 | msgid "Unable to allocate memory for interface\n" 674 | msgstr "Kunne ikke finne noen gyldige nettverksgrensesnitt\n" 675 | 676 | #: src/interfaces.c:318 677 | #, c-format 678 | msgid "Error creating network watcher thread: %s\n" 679 | msgstr "Kunne ikke starte nettverksovervåkningstråd: %s\n" 680 | 681 | #: src/interfaces.c:450 682 | #, c-format 683 | msgid "packet size too large\n" 684 | msgstr "pakkestørrelse for stor\n" 685 | 686 | #: src/protocol.c:93 src/protocol.c:158 687 | #, c-format 688 | msgid "add_control_packet: ERROR, too large packet. Exceeds %d bytes\n" 689 | msgstr "add_control_packet: FEIL, for stor pakke. Overstiger %d byte\n" 690 | 691 | #: src/protocol.c:280 692 | #, c-format 693 | msgid "mndp_add_attribute: ERROR, too large packet. Exceeds %d bytes\n" 694 | msgstr "mndp_add_attribute: FEIL, for stor pakke. Overstiger %d byte\n" 695 | 696 | #: src/protocol.c:330 697 | #, c-format 698 | msgid "%s: invalid data: %p + %u > %p + %d\n" 699 | msgstr "%s: ugyldig data: %p + %u > %p + %d\n" 700 | 701 | #: src/protocol.c:459 702 | #, c-format 703 | msgid "Unable to send broadcast packet: Router lookup will be slow\n" 704 | msgstr "Klarer ikke sende broadcast pakke: Ruter søk vil være tregt\n" 705 | 706 | #: src/protocol.c:541 707 | #, c-format 708 | msgid "Searching for '%s'..." 709 | msgstr "Søker etter '%s'..." 710 | 711 | #: src/protocol.c:545 712 | #, c-format 713 | msgid "not found\n" 714 | msgstr "ikke funnet\n" 715 | 716 | #: src/protocol.c:552 717 | #, c-format 718 | msgid "found\n" 719 | msgstr "funnet\n" 720 | 721 | #: src/mtwei.c:56 722 | #, c-format 723 | msgid "FATAL ERROR: Function returned NULL at %s:%d: %s;\n" 724 | msgstr "FATAL FEIL: Funksjonen returnerte NULL ved %s:%d: %s;\n" 725 | 726 | #: src/mtwei.c:178 727 | #, c-format 728 | msgid "Cannot mix gamma into pubkey: %s\n" 729 | msgstr "Kan ikke blande gamma inn i offentlig nøkkel: %s\n" 730 | 731 | #: src/mtwei.c:216 732 | #, c-format 733 | msgid "Cannot make a public key: %s\n" 734 | msgstr "Kan ikke lage en offentlig nøkkel: %s\n" 735 | 736 | #: src/users.c:85 737 | #, c-format 738 | msgid "Error stating file %s: %s\n" 739 | msgstr "Klarer hente info om filen %s: %s\n" 740 | 741 | #: src/users.c:91 742 | #, c-format 743 | msgid "Error getting user information for uid %d: %s\n" 744 | msgstr "Feil oppstod under henting av informasjon om uid %d: %s\n" 745 | 746 | #: src/users.c:96 747 | #, c-format 748 | msgid "Error: %s is not owned by root\n" 749 | msgstr "" 750 | "Feil: %s er ikke eid av root\n" 751 | " \n" 752 | 753 | #: src/users.c:102 754 | #, c-format 755 | msgid "" 756 | "Error: %s is writable by others, It should have permissions set to 0600 for " 757 | "better security\n" 758 | msgstr "" 759 | "Feil: %s er skrivbar for andre. Den bør ha tillatelser satt til 0600 for " 760 | "bedre sikkerhet.\n" 761 | 762 | #: src/users.c:121 763 | #, c-format 764 | msgid "Error: %s is invalid and no users known, aborting.\n" 765 | msgstr "Feil: %s er ugyldig og ingen brukere kjennes fra før, avbryter.\n" 766 | 767 | #: src/users.c:126 768 | #, c-format 769 | msgid "Warning: User file '%s' is not readable, falling back to known users.\n" 770 | msgstr "" 771 | "Advarsel: Brukerfilen '%s' er ikke lesbar, faller tilbake til tidligere " 772 | "kjente brukere.\n" 773 | 774 | #: src/users.c:157 775 | #, c-format 776 | msgid "Error allocating memory for user information\n" 777 | msgstr "Feil oppstod under minneallokering for brukerinformasjon\n" 778 | 779 | #: src/users.c:166 src/users.c:187 780 | #, c-format 781 | msgid "Warning: Invalid password hash on line %d of user file\n" 782 | msgstr "Advarsel: Ugyldig passord hash på linje %d i brukerfilen\n" 783 | 784 | #: src/users.c:171 src/users.c:178 785 | #, c-format 786 | msgid "Warning: Invalid salt on line %d of user file\n" 787 | msgstr "Advarsel: Ugyldig salt på linje %d i brukerfilen\n" 788 | 789 | #: src/users.c:236 790 | #, c-format 791 | msgid "Error opening password file %s: %s\n" 792 | msgstr "Feil oppstod under åpning av passordfilen %s: %s\n" 793 | 794 | #: src/users.c:241 795 | #, c-format 796 | msgid "Error opening temporary password file for writing %s: %s\n" 797 | msgstr "" 798 | "Feil oppstod under åpning av midlertidig passordfil for skriving %s: %s\n" 799 | 800 | #: src/users.c:247 801 | #, c-format 802 | msgid "Error changing ownership of temporary password file %s: %s\n" 803 | msgstr "Feil oppstod under eierskapsbytte av midlertidig passordfil %s: %s\n" 804 | 805 | #: src/users.c:255 806 | #, c-format 807 | msgid "Error changing permissions of temporary password file %s: %s\n" 808 | msgstr "" 809 | "Feil oppstod under endring av rettigheter på midlertidig passordfil %s: %s\n" 810 | 811 | #: src/users.c:264 812 | #, c-format 813 | msgid "Error generating random salt.\n" 814 | msgstr "Feil oppstod under generering av tilfeldig salt.\n" 815 | 816 | #, c-format 817 | #~ msgid "Usage: %s [-fnoh]\n" 818 | #~ msgstr "Bruksmåte: %s [-fnoh]\n" 819 | 820 | #, c-format 821 | #~ msgid "" 822 | #~ "\n" 823 | #~ "Parameters:\n" 824 | #~ " -f Run process in foreground.\n" 825 | #~ " -n Do not use broadcast packets. Just a tad less insecure.\n" 826 | #~ " -o Use MD5 for password hashing.\n" 827 | #~ " -h This help.\n" 828 | #~ "\n" 829 | #~ msgstr "" 830 | #~ "\n" 831 | #~ "Parametere:\n" 832 | #~ " -f Kjør prosessen i forgrunn.\n" 833 | #~ " -n Ikke bruk broadcast pakker. Bare såvidt litt mer usikkert.\n" 834 | #~ " -o Bruk gammel MD5-autentiseringsalgoritme\n" 835 | #~ " -h Denne hjelpen.\n" 836 | #~ "\n" 837 | 838 | #, c-format 839 | #~ msgid "" 840 | #~ "\n" 841 | #~ "Parameters:\n" 842 | #~ " -n Do not use broadcast packets. Just a tad less insecure.\n" 843 | #~ " -o Use MD5 for password hashing.\n" 844 | #~ " -h This help.\n" 845 | #~ "\n" 846 | #~ msgstr "" 847 | #~ "\n" 848 | #~ "Parametere:\n" 849 | #~ " -n Ikke bruk broadcast pakker. Bare såvidt litt mer usikkert.\n" 850 | #~ " -o Bruk gammel MD5-autentiseringsalgoritme\n" 851 | #~ " -h Denne hjelpen.\n" 852 | #~ "\n" 853 | 854 | #, fuzzy, c-format 855 | #~ msgid "SIGHUP: Reloading interfaces\n" 856 | #~ msgstr "SIGHUP: Laster grensesnitt på nytt" 857 | 858 | #, c-format 859 | #~ msgid "Listening on %s for %s\n" 860 | #~ msgstr "Lytter på %s for %s\n" 861 | --------------------------------------------------------------------------------