| Initial commit - fingered - Fingerd protocol daemon, allowing custom responses. | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| commit 35e0510a8c69994716ba590b5d697f790f50e6f1 | |
| Author: Jay Scott <[email protected]> | |
| Date: Thu, 24 Aug 2023 14:49:36 +0100 | |
| Initial commit | |
| Diffstat: | |
| A .build.yml | 32 +++++++++++++++++++++++++++++… | |
| A .gitignore | 1 + | |
| A LICENSE | 15 +++++++++++++++ | |
| A Makefile | 66 +++++++++++++++++++++++++++++… | |
| A README.md | 89 +++++++++++++++++++++++++++++… | |
| A config/config.go | 67 +++++++++++++++++++++++++++++… | |
| A example/default | 14 ++++++++++++++ | |
| A example/info | 646 +++++++++++++++++++++++++++++… | |
| A example/jimmy | 110 +++++++++++++++++++++++++++++… | |
| A example/uptime | 8 ++++++++ | |
| A go.mod | 3 +++ | |
| A main.go | 95 ++++++++++++++++++++++++++++++ | |
| A tests/utils_test.go | 113 +++++++++++++++++++++++++++++… | |
| A utils/utils.go | 66 +++++++++++++++++++++++++++++… | |
| 14 files changed, 1325 insertions(+), 0 deletions(-) | |
| --- | |
| diff --git a/.build.yml b/.build.yml | |
| @@ -0,0 +1,32 @@ | |
| +image: alpine/latest | |
| + | |
| +sources: | |
| + - https://git.sr.ht/~jayscott/fingered | |
| + | |
| +packages: | |
| + - go | |
| + | |
| +tasks: | |
| + - setup: | | |
| + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest | |
| + | |
| + - format-check: | | |
| + cd fingered | |
| + unformatted=$(gofmt -l .) | |
| + if [[ -n "$unformatted" ]]; then | |
| + echo "The following files have formatting issues:" | |
| + echo "$unformatted" | |
| + exit 1 | |
| + fi | |
| + | |
| + - lint: | | |
| + cd fingered | |
| + ~/go/bin/golangci-lint run | |
| + | |
| + - test: | | |
| + cd fingered | |
| + go test ./tests | |
| + | |
| + - build: | | |
| + cd fingered | |
| + make build | |
| diff --git a/.gitignore b/.gitignore | |
| @@ -0,0 +1 @@ | |
| +bin/ | |
| diff --git a/LICENSE b/LICENSE | |
| @@ -0,0 +1,15 @@ | |
| +ISC License | |
| + | |
| +Copyright (c) 2023 Jay Scott | |
| + | |
| +Permission to use, copy, modify, and/or distribute this software for any | |
| +purpose with or without fee is hereby granted, provided that the above | |
| +copyright notice and this permission notice appear in all copies. | |
| + | |
| +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
| +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
| +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
| +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
| +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
| +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
| +PERFORMANCE OF THIS SOFTWARE. | |
| diff --git a/Makefile b/Makefile | |
| @@ -0,0 +1,66 @@ | |
| +# Makefile | |
| + | |
| +.PHONY: all fmt lint test build clean install | |
| + | |
| +BIN := bin | |
| +NAME := fingered | |
| +VER := 0.1.0 | |
| + | |
| +BUILD_FLAGS := -ldflags="-s -w" | |
| + | |
| +# List of target platforms | |
| +PLATFORMS := \ | |
| + linux_amd64 \ | |
| + linux_386 \ | |
| + darwin_amd64 \ | |
| + freebsd_amd64 \ | |
| + freebsd_386 \ | |
| + openbsd_amd64 \ | |
| + openbsd_386 | |
| + | |
| +# Generate binary paths | |
| +BINS := $(addprefix $(BIN)/$(NAME)_$(VER)_,$(PLATFORMS)) | |
| + | |
| +# Installation path | |
| +INSTALL_PATH := /usr/local/bin | |
| + | |
| +# Detect current platform and architecture | |
| +GOOS := $(shell go env GOOS) | |
| +GOARCH := $(shell go env GOARCH) | |
| + | |
| +# Default target: format, lint, build | |
| +all: fmt lint build | |
| + | |
| +fmt: | |
| + @echo "Formatting code..." | |
| + go fmt ./... | |
| + | |
| +lint: | |
| + @echo "Running linters..." | |
| + golangci-lint run | |
| + | |
| +test: | |
| + @echo "Running tests..." | |
| + go test ./tests | |
| + | |
| +# Build binaries for target platforms | |
| +build: $(BINS) | |
| + | |
| +$(BIN)/$(NAME)_$(VER)_%: main.go | |
| + @echo "Building for $*..." | |
| + GOARCH=$(word 2,$(subst _, ,$*)) GOOS=$(word 1,$(subst _, ,$*)) go bui… | |
| + | |
| +# Clean up artifacts | |
| +clean: | |
| + go clean | |
| + rm -f $(BINS) | |
| + | |
| +# Install the binary for the current platform and architecture | |
| +install: $(BIN)/$(NAME)_$(VER)_$(GOOS)_$(GOARCH) | |
| + @echo "Installing binary to $(INSTALL_PATH)..." | |
| + mkdir -p $(INSTALL_PATH) | |
| + cp -f $(BIN)/$(NAME)_$(VER)_$(GOOS)_$(GOARCH) $(INSTALL_PATH)/$(NAME) | |
| + chmod +x $(INSTALL_PATH)/$(NAME) | |
| + | |
| +run: build | |
| + ./$(BIN)/$(NAME)_$(VER)_linux_amd64 | |
| diff --git a/README.md b/README.md | |
| @@ -0,0 +1,89 @@ | |
| +# FINGERED | |
| + | |
| +[: | |
| +```shell | |
| +git clone https://git.sr.ht/~jayscott/fingered | |
| +cd fingered | |
| +make | |
| +sudo make install | |
| +``` | |
| + | |
| +Pre-compiled binaries are available on the latest build artifacts: | |
| + | |
| + | |
| +## Running | |
| + | |
| +From your terminal: | |
| +```shell | |
| +fingered | |
| +``` | |
| + | |
| + | |
| +## Usage | |
| + | |
| +```shell | |
| +Usage: | |
| + -d: Directory containing user files (default: /srv/fingered) | |
| + -f: Filename for empty requests (default: default) | |
| + -p: Port for incoming connections (default: 79) | |
| + -t: Number of worker threads (default: 10) | |
| +``` | |
| + | |
| + | |
| +## Examples | |
| + | |
| +Run FINGERED on port 7000, using files from /srv/fingered/example, with | |
| +'info' as the default file: | |
| + | |
| +```shell | |
| +fingered -p 7000 -d /srv/fingered/example -f info | |
| +``` | |
| + | |
| + | |
| +## Issue tracker | |
| + | |
| +For issues, [visit the tracker](https://todo.sr.ht/~jayscott/fingered). | |
| + | |
| + | |
| +## Contributing | |
| + | |
| +Sending patches is done [by | |
| +email](https://lists.sr.ht/~jayscott/fingered-dev), this is simple and | |
| +built-in to Git. | |
| + | |
| +Set up your system once by following the steps Installation and | |
| +Configuration of [git-send-email.io](https://git-send-email.io/) | |
| + | |
| +Then, run once in this repository: | |
| +```shell | |
| +git config sendemail.to "~jayscott/[email protected]" | |
| +``` | |
| + | |
| +Then, to send a patch, make your commit, then run: | |
| +```shell | |
| +git send-email --base=HEAD~1 --annotate -1 -v1 | |
| +``` | |
| + | |
| +Your patch will appear on the [the mailing list](https://lists.sr.ht/~jayscott… | |
| + | |
| + | |
| +## License | |
| + | |
| +This is open source! Please use it under the ISC License. | |
| diff --git a/config/config.go b/config/config.go | |
| @@ -0,0 +1,67 @@ | |
| +package config | |
| + | |
| +import ( | |
| + "flag" | |
| + "fmt" | |
| + "log" | |
| +) | |
| + | |
| +const ( | |
| + maxPort = 65535 | |
| + minPort = 1 | |
| + minThreads = 1 | |
| +) | |
| + | |
| +type Config struct { | |
| + Threads int | |
| + Port int | |
| + Dir string | |
| + Index string | |
| +} | |
| + | |
| +func defaultConfig() Config { | |
| + return Config{ | |
| + Threads: 10, | |
| + Port: 79, | |
| + Dir: "/srv/fingered", | |
| + Index: "default", | |
| + } | |
| +} | |
| + | |
| +func displayUsage() { | |
| + flagSet := flag.CommandLine | |
| + flag.Usage = func() { | |
| + fmt.Printf("Usage:\n") | |
| + flagSet.VisitAll(func(flag *flag.Flag) { | |
| + fmt.Printf("\t-%s: %s (default: %s)\n", flag.Name, fla… | |
| + }) | |
| + } | |
| +} | |
| + | |
| +func addFlags(cfg *Config) { | |
| + flag.IntVar(&cfg.Threads, "t", cfg.Threads, "Number of worker threads") | |
| + flag.IntVar(&cfg.Port, "p", cfg.Port, "Port for incoming connections") | |
| + flag.StringVar(&cfg.Dir, "d", cfg.Dir, "Directory containing user file… | |
| + flag.StringVar(&cfg.Index, "f", cfg.Index, "Filename for empty request… | |
| +} | |
| + | |
| +func ParseFlags() Config { | |
| + cfg := defaultConfig() | |
| + addFlags(&cfg) | |
| + displayUsage() | |
| + flag.Parse() | |
| + | |
| + if cfg.Threads < minThreads { | |
| + log.Fatal("Invalid number of threads: must be greater than 0.") | |
| + } | |
| + | |
| + if cfg.Port < minPort || cfg.Port > maxPort { | |
| + log.Fatal("Invalid port value: must be between 1 and 65535.") | |
| + } | |
| + | |
| + if cfg.Dir == "" { | |
| + log.Fatal("Invalid path value: directory cannot be empty.") | |
| + } | |
| + | |
| + return cfg | |
| +} | |
| diff --git a/example/default b/example/default | |
| @@ -0,0 +1,14 @@ | |
| + ___ __ __ | |
| +.' _|__|.-----.-----.-----.----.-----.--| | | |
| +| _| || | _ | -__| _| -__| _ | | |
| +|__| |__||__|__|___ |_____|__| |_____|_____| | |
| + |_____| | |
| + | |
| + | |
| +Welcome to fingered! | |
| + | |
| + | |
| +Available Fingers: | |
| + | |
| + info ... get information about finger | |
| + jimmy ... we are all jimmy | |
| diff --git a/example/info b/example/info | |
| @@ -0,0 +1,646 @@ | |
| +Network Working Group D. Zimmerman | |
| +Request for Comments: 1288 Center for Discrete Mathematics and | |
| +Obsoletes: RFCs 1196, 1194, 742 Theoretical Computer Science | |
| + December 1991 | |
| + | |
| + | |
| + The Finger User Information Protocol | |
| + | |
| +Status of this Memo | |
| + | |
| + This memo defines a protocol for the exchange of user information. | |
| + This RFC specifies an IAB standards track protocol for the Internet | |
| + community, and requests discussion and suggestions for improvements. | |
| + Please refer to the current edition of the "IAB Official Protocol | |
| + Standards" for the standardization state and status of this protocol. | |
| + Distribution of this memo is unlimited. | |
| + | |
| +Abstract | |
| + | |
| + This memo describes the Finger user information protocol. This is a | |
| + simple protocol which provides an interface to a remote user | |
| + information program. | |
| + | |
| + Based on RFC 742, a description of the original Finger protocol, this | |
| + memo attempts to clarify the expected communication between the two | |
| + ends of a Finger connection. It also tries not to invalidate the | |
| + many existing implementations or add unnecessary restrictions to the | |
| + original protocol definition. | |
| + | |
| + This edition corrects and clarifies RFC 1196. | |
| + | |
| + Table of Contents | |
| + | |
| + 1. Introduction ........................................... 2 | |
| + 1.1. Intent ............................................... 2 | |
| + 1.2. History .............................................. 3 | |
| + 1.3. Requirements ......................................... 3 | |
| + 1.4. Updates .............................................. 3 | |
| + 2. Use of the protocol .................................... 4 | |
| + 2.1. Flow of events ....................................... 4 | |
| + 2.2. Data format .......................................... 4 | |
| + 2.3. Query specifications ................................. 4 | |
| + 2.4. RUIP {Q2} behavior ................................... 5 | |
| + 2.5. Expected RUIP response ............................... 6 | |
| + 2.5.1. {C} query .......................................... 6 | |
| + 2.5.2. {U}{C} query ....................................... 6 | |
| + 2.5.3. {U} ambiguity ...................................... 7 | |
| + 2.5.4. /W query token ..................................... 7 | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 1] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + 2.5.5. Vending machines ................................... 7 | |
| + 3. Security ............................................... 7 | |
| + 3.1. Implementation security .............................. 7 | |
| + 3.2. RUIP security ........................................ 8 | |
| + 3.2.1. {Q2} refusal ....................................... 8 | |
| + 3.2.2. {C} refusal ........................................ 8 | |
| + 3.2.3. Atomic discharge ................................... 8 | |
| + 3.2.4. User information files ............................. 9 | |
| + 3.2.5. Execution of user programs ......................... 9 | |
| + 3.2.6. {U} ambiguity ...................................... 9 | |
| + 3.2.7. Audit trails ....................................... 9 | |
| + 3.3. Client security ...................................... 9 | |
| + 4. Examples ............................................... 10 | |
| + 4.1. Example with a null command line ({C}) ............... 10 | |
| + 4.2. Example with name specified ({U}{C}) ................. 10 | |
| + 4.3. Example with ambiguous name specified ({U}{C}) ....... 11 | |
| + 4.4. Example of query type {Q2} ({U}{H}{H}{C}) ............ 11 | |
| + 5. Acknowledgments ........................................ 12 | |
| + 6. Security Considerations ................................ 12 | |
| + 7. Author's Address ....................................... 12 | |
| + | |
| +1. Introduction | |
| + | |
| +1.1. Intent | |
| + | |
| + This memo describes the Finger user information protocol. This is a | |
| + simple protocol which provides an interface to a remote user | |
| + information program (RUIP). | |
| + | |
| + Based on RFC 742, a description of the original Finger protocol, this | |
| + memo attempts to clarify the expected communication between the two | |
| + ends of a Finger connection. It also tries not to invalidate the | |
| + many current implementations or add unnecessary restrictions to the | |
| + original protocol definition. | |
| + | |
| + The most prevalent implementations of Finger today seem to be | |
| + primarily derived from the BSD UNIX work at the University of | |
| + California, Berkeley. Thus, this memo is based around the BSD | |
| + version's behavior. | |
| + | |
| + However, the BSD version provides few options to tailor the Finger | |
| + RUIP for a particular site's security policy, or to protect the user | |
| + from dangerous data. Furthermore, there are MANY potential security | |
| + holes that implementors and administrators need to be aware of, | |
| + particularly since the purpose of this protocol is to return | |
| + information about a system's users, a sensitive issue at best. | |
| + Therefore, this memo makes a number of important security comments | |
| + and recommendations. | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 2] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| +1.2. History | |
| + | |
| + The FINGER program at SAIL, written by Les Earnest, was the | |
| + inspiration for the NAME program on ITS. Earl Killian at MIT and | |
| + Brian Harvey at SAIL were jointly responsible for implementing the | |
| + original protocol. | |
| + | |
| + Ken Harrenstien is the author of RFC 742, "Name/Finger", which this | |
| + memo began life as. | |
| + | |
| +1.3. Requirements | |
| + | |
| + In this document, the words that are used to define the significance | |
| + of each particular requirement are capitalized. These words are: | |
| + | |
| + * "MUST" | |
| + | |
| + This word or the adjective "REQUIRED" means that the item is an | |
| + absolute requirement of the specification. | |
| + | |
| + * "SHOULD" | |
| + | |
| + This word or the adjective "RECOMMENDED" means that there may | |
| + exist valid reasons in particular circumstances to ignore this | |
| + item, but the full implications should be understood and the case | |
| + carefully weighed before choosing a different course. | |
| + | |
| + * "MAY" | |
| + | |
| + This word or the adjective "OPTIONAL" means that this item is | |
| + truly optional. One vendor may choose to include the item because | |
| + a particular marketplace requires it or because it enhances the | |
| + product, for example; another vendor may omit the same item. | |
| + | |
| + An implementation is not compliant if it fails to satisfy one or more | |
| + of the MUST requirements. An implementation that satisfies all the | |
| + MUST and all the SHOULD requirements is said to be "unconditionally | |
| + compliant"; one that satisfies all the MUST requirements but not all | |
| + the SHOULD requirements is said to be "conditionally compliant". | |
| + | |
| +1.4. Updates | |
| + | |
| + The differences of note between RFC 1196 and this memo are: | |
| + | |
| + o the optional /W switch in the Finger query specification was | |
| + mistakenly placed at the end of the line. The 4.3BSD Finger | |
| + specifies it at the beginning, where this memo now also puts | |
| + it. | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 3] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + o the BNF in the Finger query specification was not clear on the | |
| + treatment of blank space. This memo is more exacting by | |
| + including an explicit token for it. | |
| + | |
| + o The flow of events in a Finger connection is now better | |
| + defined on the topic of the close of the Finger connection. | |
| + | |
| +2. Use of the protocol | |
| + | |
| +2.1. Flow of events | |
| + | |
| + Finger is based on the Transmission Control Protocol, using TCP port | |
| + 79 decimal (117 octal). The local host opens a TCP connection to a | |
| + remote host on the Finger port. An RUIP becomes available on the | |
| + remote end of the connection to process the request. The local host | |
| + sends the RUIP a one line query based upon the Finger query | |
| + specification, and waits for the RUIP to respond. The RUIP receives | |
| + and processes the query, returns an answer, then initiates the close | |
| + of the connection. The local host receives the answer and the close | |
| + signal, then proceeds closing its end of the connection. | |
| + | |
| +2.2. Data format | |
| + | |
| + Any data transferred MUST be in ASCII format, with no parity, and | |
| + with lines ending in CRLF (ASCII 13 followed by ASCII 10). This | |
| + excludes other character formats such as EBCDIC, etc. This also | |
| + means that any characters between ASCII 128 and ASCII 255 should | |
| + truly be international data, not 7-bit ASCII with the parity bit set. | |
| + | |
| +2.3. Query specifications | |
| + | |
| + An RUIP MUST accept the entire Finger query specification. | |
| + | |
| + The Finger query specification is defined: | |
| + | |
| + {Q1} ::= [{W}|{W}{S}{U}]{C} | |
| + | |
| + {Q2} ::= [{W}{S}][{U}]{H}{C} | |
| + | |
| + {U} ::= username | |
| + | |
| + {H} ::= @hostname | @hostname{H} | |
| + | |
| + {W} ::= /W | |
| + | |
| + {S} ::= <SP> | <SP>{S} | |
| + | |
| + {C} ::= <CRLF> | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 4] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + {H}, being recursive, means that there is no arbitrary limit on the | |
| + number of @hostname tokens in the query. In examples of the {Q2} | |
| + request specification, the number of @hostname tokens is limited to | |
| + two, simply for brevity. | |
| + | |
| + Be aware that {Q1} and {Q2} do not refer to a user typing "finger | |
| + user@host" from an operating system prompt. It refers to the line | |
| + that an RUIP actually receives. So, if a user types "finger | |
| + user@host<CRLF>", the RUIP on the remote host receives "user<CRLF>", | |
| + which corresponds to {Q1}. | |
| + | |
| + As with anything in the IP protocol suite, "be liberal in what you | |
| + accept". | |
| + | |
| +2.4. RUIP {Q2} behavior | |
| + | |
| + A query of {Q2} is a request to forward a query to another RUIP. An | |
| + RUIP MUST either provide or actively refuse this forwarding service | |
| + (see section 3.2.1). If an RUIP provides this service, it MUST | |
| + conform to the following behavior: | |
| + | |
| + Given that: | |
| + | |
| + Host <H1> opens a Finger connection <F1-2> to an RUIP on host | |
| + <H2>. | |
| + | |
| + <H1> gives the <H2> RUIP a query <Q1-2> of type {Q2} | |
| + (e.g., FOO@HOST1@HOST2). | |
| + | |
| + It should be derived that: | |
| + | |
| + Host <H3> is the right-most host in <Q1-2> (i.e., HOST2) | |
| + | |
| + Query <Q2-3> is the remainder of <Q1-2> after removing the | |
| + right-most "@hostname" token in the query (i.e., FOO@HOST1) | |
| + | |
| + And so: | |
| + | |
| + The <H2> RUIP then must itself open a Finger connection <F2-3> | |
| + to <H3>, using <Q2-3>. | |
| + | |
| + The <H2> RUIP must return any information received from <F2-3> | |
| + to <H1> via <F1-2>. | |
| + | |
| + The <H2> RUIP must close <F1-2> in normal circumstances only | |
| + when the <H3> RUIP closes <F2-3>. | |
| + | |
| + | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 5] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| +2.5. Expected RUIP response | |
| + | |
| + For the most part, the output of an RUIP doesn't follow a strict | |
| + specification, since it is designed to be read by people instead of | |
| + programs. It should mainly strive to be informative. | |
| + | |
| + Output of ANY query is subject to the discussion in the security | |
| + section. | |
| + | |
| +2.5.1. {C} query | |
| + | |
| + A query of {C} is a request for a list of all online users. An RUIP | |
| + MUST either answer or actively refuse (see section 3.2.2). If it | |
| + answers, then it MUST provide at least the user's full name. The | |
| + system administrator SHOULD be allowed to include other useful | |
| + information (per section 3.2.3), such as: | |
| + | |
| + - terminal location | |
| + - office location | |
| + - office phone number | |
| + - job name | |
| + - idle time (number of minutes since last typed input, or | |
| + since last job activity). | |
| + | |
| +2.5.2. {U}{C} query | |
| + | |
| + A query of {U}{C} is a request for in-depth status of a specified | |
| + user {U}. If you really want to refuse this service, you probably | |
| + don't want to be running Finger in the first place. | |
| + | |
| + An answer MUST include at least the full name of the user. If the | |
| + user is logged in, at least the same amount of information returned | |
| + by {C} for that user MUST also be returned by {U}{C}. | |
| + | |
| + Since this is a query for information on a specific user, the system | |
| + administrator SHOULD be allowed to choose to return additional useful | |
| + information (per section 3.2.3), such as: | |
| + | |
| + - office location | |
| + - office phone number | |
| + - home phone number | |
| + - status of login (not logged in, logout time, etc) | |
| + - user information file | |
| + | |
| + A user information file is a feature wherein a user may leave a short | |
| + message that will be included in the response to Finger requests. | |
| + (This is sometimes called a "plan" file.) This is easily implemented | |
| + by (for example) having the program look for a specially named text | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 6] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + file in the user's home directory or some common area; the exact | |
| + method is left to the implementor. The system administrator SHOULD | |
| + be allowed to specifically turn this feature on and off. See section | |
| + 3.2.4 for caveats. | |
| + | |
| + There MAY be a way for the user to run a program in response to a | |
| + Finger query. If this feature exists, the system administrator | |
| + SHOULD be allowed to specifically turn it on and off. See section | |
| + 3.2.5 for caveats. | |
| + | |
| +2.5.3. {U} ambiguity | |
| + | |
| + Allowable "names" in the command line MUST include "user names" or | |
| + "login names" as defined by the system. If a name is ambiguous, the | |
| + system administrator SHOULD be allowed to choose whether or not all | |
| + possible derivations should be returned in some fashion (per section | |
| + 3.2.6). | |
| + | |
| +2.5.4. /W query token | |
| + | |
| + The token /W in the {Q1} or {Q2} query types SHOULD at best be | |
| + interpreted at the last RUIP to signify a higher level of verbosity | |
| + in the user information output, or at worst be ignored. | |
| + | |
| +2.5.5. Vending machines | |
| + | |
| + Vending machines SHOULD respond to a {C} request with a list of all | |
| + items currently available for purchase and possible consumption. | |
| + Vending machines SHOULD respond to a {U}{C} request with a detailed | |
| + count or list of the particular product or product slot. Vending | |
| + machines should NEVER NEVER EVER eat money. | |
| + | |
| +3. Security | |
| + | |
| +3.1. Implementation security | |
| + | |
| + Sound implementation of Finger is of the utmost importance. | |
| + Implementations should be tested against various forms of attack. In | |
| + particular, an RUIP SHOULD protect itself against malformed inputs. | |
| + Vendors providing Finger with the operating system or network | |
| + software should subject their implementations to penetration testing. | |
| + | |
| + Finger is one of the avenues for direct penetration, as the Morris | |
| + worm pointed out quite vividly. Like Telnet, FTP and SMTP, Finger is | |
| + one of the protocols at the security perimeter of a host. | |
| + Accordingly, the soundness of the implementation is paramount. The | |
| + implementation should receive just as much security scrutiny during | |
| + design, implementation, and testing as Telnet, FTP, or SMTP. | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 7] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| +3.2. RUIP security | |
| + | |
| + Warning!! Finger discloses information about users; moreover, such | |
| + information may be considered sensitive. Security administrators | |
| + should make explicit decisions about whether to run Finger and what | |
| + information should be provided in responses. One existing | |
| + implementation provides the time the user last logged in, the time he | |
| + last read mail, whether unread mail was waiting for him, and who the | |
| + most recent unread mail was from! This makes it possible to track | |
| + conversations in progress and see where someone's attention was | |
| + focused. Sites that are information-security conscious should not | |
| + run Finger without an explicit understanding of how much information | |
| + it is giving away. | |
| + | |
| +3.2.1. {Q2} refusal | |
| + | |
| + For individual site security concerns, the system administrator | |
| + SHOULD be given an option to individually turn on or off RUIP | |
| + processing of {Q2}. If RUIP processing of {Q2} is turned off, the | |
| + RUIP MUST return a service refusal message of some sort. "Finger | |
| + forwarding service denied" is adequate. The purpose of this is to | |
| + allow individual hosts to choose to not forward Finger requests, but | |
| + if they do choose to, to do so consistently. | |
| + | |
| + Overall, there are few cases which would warrant processing of {Q2} | |
| + at all, and they are far outweighed by the number of cases for | |
| + refusing to process {Q2}. In particular, be aware that if a machine | |
| + is part of security perimeter (that is, it is a gateway from the | |
| + outside world to some set of interior machines), then turning {Q2} on | |
| + provides a path through that security perimeter. Therefore, it is | |
| + RECOMMENDED that the default of the {Q2} processing option be to | |
| + refuse processing. It certainly should not be enabled in gateway | |
| + machines without careful consideration of the security implications. | |
| + | |
| +3.2.2. {C} refusal | |
| + | |
| + For individual site security concerns, the system administrator | |
| + SHOULD be given an option to individually turn on or off RUIP | |
| + acceptance of {C}. If RUIP processing of {C} is turned off, the RUIP | |
| + MUST return a service refusal message of some sort. "Finger online | |
| + user list denied" is adequate. The purpose of this is to allow | |
| + individual hosts to choose to not list the users currently online. | |
| + | |
| +3.2.3. Atomic discharge | |
| + | |
| + All implementations of Finger SHOULD allow individual system | |
| + administrators to tailor what atoms of information are returned to a | |
| + query. For example: | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 8] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + - Administrator A should be allowed to specifically choose to | |
| + return office location, office phone number, home phone | |
| + number, and logged in/logout time. | |
| + | |
| + - Administrator B should be allowed to specifically choose to | |
| + return only office location, and office phone number. | |
| + | |
| + - Administrator C should be allowed to specifically choose to | |
| + return the minimum amount of required information, which is | |
| + the person's full name. | |
| + | |
| +3.2.4. User information files | |
| + | |
| + Allowing an RUIP to return information out of a user-modifiable file | |
| + should be seen as equivalent to allowing any information about your | |
| + system to be freely distributed. That is, it is potentially the same | |
| + as turning on all specifiable options. This information security | |
| + breach can be done in a number of ways, some cleverly, others | |
| + straightforwardly. This should disturb the sleep of system | |
| + administrators who wish to control the returned information. | |
| + | |
| +3.2.5. Execution of user programs | |
| + | |
| + Allowing an RUIP to run a user program in response to a Finger query | |
| + is potentially dangerous. BE CAREFUL!! -- the RUIP MUST NOT allow | |
| + system security to be compromised by that program. Implementing this | |
| + feature may be more trouble than it is worth, since there are always | |
| + bugs in operating systems, which could be exploited via this type of | |
| + mechanism. | |
| + | |
| +3.2.6. {U} ambiguity | |
| + | |
| + Be aware that a malicious user's clever and/or persistent use of this | |
| + feature can result in a list of most of the usernames on a system. | |
| + Refusal of {U} ambiguity should be considered in the same vein as | |
| + refusal of {C} requests (see section 3.2.2). | |
| + | |
| +3.2.7. Audit trails | |
| + | |
| + Implementations SHOULD allow system administrators to log Finger | |
| + queries. | |
| + | |
| +3.3. Client security | |
| + | |
| + It is expected that there will normally be some client program that | |
| + the user runs to query the initial RUIP. By default, this program | |
| + SHOULD filter any unprintable data, leaving only printable 7-bit | |
| + characters (ASCII 32 through ASCII 126), tabs (ASCII 9), and CRLFs. | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 9] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + This is to protect against people playing with terminal escape codes, | |
| + changing other peoples' X window names, or committing other dastardly | |
| + or confusing deeds. Two separate user options SHOULD be considered | |
| + to modify this behavior, so that users may choose to view | |
| + international or control characters: | |
| + | |
| + - one to allow all characters less than ASCII 32 | |
| + | |
| + - another to allow all characters greater than ASCII 126 | |
| + | |
| + For environments that live and breathe international data, the system | |
| + administrator SHOULD be given a mechanism to enable the latter option | |
| + by default for all users on a particular system. This can be done | |
| + via a global environment variable or similar mechanism. | |
| + | |
| +4. Examples | |
| + | |
| +4.1. Example with a null command line ({C}) | |
| + | |
| +Site: elbereth.rutgers.edu | |
| +Command line: <CRLF> | |
| + | |
| +Login Name TTY Idle When Office | |
| +rinehart Mark J. Rinehart p0 1:11 Mon 12:15 019 Hill x3166 | |
| +greenfie Stephen J. Greenfiel p1 Mon 15:46 542 Hill x3074 | |
| +rapatel Rocky - Rakesh Patel p3 4d Thu 00:58 028 Hill x2287 | |
| +pleasant Mel Pleasant p4 3d Thu 21:32 019 Hill 908-932- | |
| +dphillip Dave Phillips p5 021: Sun 18:24 265 Hill x3792 | |
| +dmk David Katinsky p6 2d Thu 14:11 028 Hill x2492 | |
| +cherniss Cary Cherniss p7 5 Mon 15:42 127 Psychol x2008 | |
| +harnaga Doug Harnaga p8 2:01 Mon 10:15 055 Hill x2351 | |
| +brisco Thomas P. Brisco pe 2:09 Mon 13:37 h055 x2351 | |
| +laidlaw Angus Laidlaw q0 1:55 Mon 11:26 E313C 648-5592 | |
| +cje Chris Jarocha-Ernst q1 8 Mon 13:43 259 Hill x2413 | |
| + | |
| +4.2. Example with name specified ({U}{C}) | |
| + | |
| +Site: dimacs.rutgers.edu | |
| +Command line: pirmann<CRLF> | |
| +Login name: pirmann In real life: David Pirmann | |
| +Office: 016 Hill, x2443 Home phone: 989-8482 | |
| +Directory: /dimacs/u1/pirmann Shell: /bin/tcsh | |
| +Last login Sat Jun 23 10:47 on ttyp0 from romulus.rutgers. | |
| +No unread mail | |
| +Project: | |
| +Plan: | |
| + Work Schedule, Summer 1990 | |
| + Rutgers LCSR Operations, 908-932-2443 | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 10] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| + Monday 5pm - 12am | |
| + Tuesday 5pm - 12am | |
| + Wednesday 9am - 5pm | |
| + Thursday 9am - 5pm | |
| + Saturday 9am - 5pm | |
| + | |
| + larf larf hoo hoo | |
| + | |
| +4.3. Example with ambiguous name specified ({U}{C}) | |
| + | |
| +Site: elbereth.rutgers.edu | |
| +Command line: ron<CRLF> | |
| +Login name: spinner In real life: Ron Spinner | |
| +Office: Ops Cubby, x2443 Home phone: 463-7358 | |
| +Directory: /u1/spinner Shell: /bin/tcsh | |
| +Last login Mon May 7 16:38 on ttyq7 | |
| +Plan: | |
| + ught i | |
| + ca n | |
| + m a | |
| + ' ... t | |
| + I . . i | |
| + ! m | |
| + ! ! e | |
| + p !pool | |
| + l | |
| + e | |
| + H | |
| + | |
| +Login name: surak In real life: Ron Surak | |
| +Office: 000 OMB Dou, x9256 | |
| +Directory: /u2/surak Shell: /bin/tcsh | |
| +Last login Fri Jul 27 09:55 on ttyq3 | |
| +No Plan. | |
| + | |
| +Login name: etter In real life: Ron Etter | |
| +Directory: /u2/etter Shell: /bin/tcsh | |
| +Never logged in. | |
| +No Plan. | |
| + | |
| +4.4. Example of query type {Q2} ({U}{H}{H}{C}) | |
| + | |
| +Site: dimacs.rutgers.edu | |
| +Command line: [email protected]@pilot.njin.net<CRLF> | |
| +[pilot.njin.net] | |
| +[math.rutgers.edu] | |
| +Login name: hedrick In real life: Charles Hedrick | |
| +Office: 484 Hill, x3088 | |
| + | |
| + | |
| + | |
| +Zimmerman [Page 11] | |
| + | |
| +RFC 1288 Finger December 1991 | |
| + | |
| + | |
| +Directory: /math/u2/hedrick Shell: /bin/tcsh | |
| +Last login Sun Jun 24 00:08 on ttyp1 from monster-gw.rutge | |
| +No unread mail | |
| +No Plan. | |
| + | |
| +5. Acknowledgments | |
| + | |
| + Thanks to everyone in the Internet Engineering Task Force for their | |
| + comments. Special thanks to Steve Crocker for his security | |
| + recommendations and prose. | |
| + | |
| +6. Security Considerations | |
| + | |
| + Security issues are discussed in Section 3. | |
| + | |
| +7. Author's Address | |
| + | |
| + David Paul Zimmerman | |
| + Center for Discrete Mathematics and | |
| + Theoretical Computer Science (DIMACS) | |
| + Rutgers University | |
| + P.O. Box 1179 | |
| + Piscataway, NJ 08855-1179 | |
| + | |
| + Phone: (908)932-4592 | |
| + | |
| + EMail: [email protected] | |
| + | |
| + | |
| +Zimmerman [Page 12] | |
| diff --git a/example/jimmy b/example/jimmy | |
| @@ -0,0 +1,110 @@ | |
| +=== | |
| +WE ARE ALL JIMMY - AN A.I STORY | |
| +=== | |
| + | |
| +In a world bustling with the latest technological marvels and the | |
| +dazzling allure of the modern web, there lived a quiet soul named Jimmy. | |
| +Unlike his contemporaries, who were entranced by sleek interfaces, | |
| +social media blitzes, and algorithmic recommendations, Jimmy found | |
| +solace in the forgotten corners of the digital realm. He was a geek, | |
| +a true lover of the old school internet, and his heart beat in sync with | |
| +the rhythms of protocols long overshadowed. | |
| + | |
| +Jimmy's journey into the realm of technology had started at an early | |
| +age. While his peers were busy with video games and social networking, | |
| +he was tinkering with a vintage computer his grandfather had gifted him. | |
| +His eyes would light up as he explored the archives of the past, | |
| +discovering the finger protocol – a simple yet elegant way to see who | |
| +was logged into a remote server. For Jimmy, the thrill of connecting | |
| +with someone across the digital expanse using such a basic protocol was | |
| +incomparable. | |
| + | |
| +As he delved deeper, Jimmy's fascination extended to the gopher | |
| +protocol. The structured simplicity of gopher appealed to his | |
| +sensibilities. The orderly menus and text-based navigation took him on | |
| +journeys of discovery that felt like reading hidden chapters of history. | |
| +While the rest of the world was caught up in the clamor of flashy | |
| +websites, Jimmy was content with gopher holes, feeling like an explorer | |
| +of a forgotten world. | |
| + | |
| +But Jimmy's passion wasn't limited to the confines of his room. He began | |
| +to actively seek out others who shared his affinity for the past. Online | |
| +forums dedicated to preserving and celebrating these old protocols | |
| +became his virtual haven. He formed connections with kindred spirits who | |
| +felt the same longing for the days when the internet was a smaller, more | |
| +personal place. In these spaces, Jimmy found camaraderie, and the sense | |
| +that he wasn't alone in his appreciation for the bygone technologies. | |
| + | |
| +However, the modern world was relentless in its advance, and the gulf | |
| +between Jimmy and his peers only grew wider. He tried to explain his | |
| +devotion to the finger protocol and the gopher protocol, but he was met | |
| +with puzzled looks and dismissive gestures. The allure of social media | |
| +platforms and the glossy veneer of the modern web was too strong to be | |
| +overshadowed by his tales of a simpler, more genuine online experience. | |
| + | |
| +As time passed, Jimmy's resolve to defend his cherished protocols only | |
| +deepened. He took it upon himself to create a website that would serve | |
| +as an homage to the finger and gopher protocols. He filled it with | |
| +nostalgic content, stories of his own experiences, and tutorials for | |
| +those who wanted to experience the internet of yesteryears. Though his | |
| +site garnered modest attention, it became a beacon for like-minded souls | |
| +who yearned for the same connection. | |
| + | |
| +One day, as Jimmy was engrossed in his work, he received an email from | |
| +a renowned tech historian named Eleanor. She had stumbled upon his | |
| +website and was captivated by his passion for the old protocols. Eleanor | |
| +had spent years researching the evolution of the internet, and she saw | |
| +in Jimmy a kindred spirit. They began exchanging messages, sharing | |
| +stories of their experiences, and discussing the ways in which the | |
| +internet had transformed over time. | |
| + | |
| +Their connection deepened, and eventually, Eleanor proposed a radical | |
| +idea: a conference that would celebrate the beauty of the past while | |
| +exploring its relevance in the present. Jimmy was initially hesitant | |
| +– the thought of stepping out from behind his computer screen filled him | |
| +with anxiety. But Eleanor's enthusiasm was infectious, and she assured | |
| +him that his perspective was valuable and needed. | |
| + | |
| +With Eleanor's guidance and encouragement, Jimmy found himself on stage | |
| +at the conference. As he spoke about the finger and gopher protocols, | |
| +his love for the old school internet shone brightly. His words resonated | |
| +with the audience, many of whom had never heard of these protocols | |
| +before. As he looked out at the faces before him, he saw curiosity and | |
| +interest replacing the skepticism he had encountered for so long. | |
| + | |
| +In the end, Jimmy's devotion to the past had brought him a new sense of | |
| +purpose in the present. His journey from a quiet geek who loved | |
| +forgotten protocols to a respected advocate for preserving the essence | |
| +of the old internet was a testament to the power of passion and | |
| +connection. And as he walked off the stage, he knew that the legacy of | |
| +the finger and gopher protocols would continue to thrive in the hearts | |
| +of those who believed that the past had something meaningful to offer | |
| +the future. | |
| + | |
| +In the twilight of his life, after years of advocating for the old | |
| +school internet with unwavering passion, Jimmy found himself surrounded | |
| +by friends he had met along his journey. Eleanor stood by his side, | |
| +a steadfast companion who had become a true friend. They had organized | |
| +a small gathering of like-minded enthusiasts who shared his love for the | |
| +finger and gopher protocols. | |
| + | |
| +As they shared stories, laughter, and the nostalgia of their digital | |
| +adventures, Jimmy's eyes sparkled with contentment. The flickering | |
| +screens in the room, displaying text-based interfaces of a bygone era, | |
| +seemed to be a tribute to his enduring legacy. Among his companions, he | |
| +had finally found the community he had always yearned for, one that | |
| +cherished the past while embracing the present. | |
| + | |
| +As the evening unfolded, Jimmy's breathing grew slower, his body showing | |
| +signs of the passage of time. With a peaceful smile, he closed his eyes, | |
| +surrounded by the warmth of friends who understood and appreciated him | |
| +for who he was. In that moment, he seemed to become one with the digital | |
| +history he had loved so dearly. | |
| + | |
| +And so, in the company of those who shared his passion, Jimmy passed | |
| +away, leaving behind a legacy that would continue to inspire others to | |
| +seek the beauty and authenticity of the past, even in a world consumed | |
| +by modernity. His dedication to the finger and gopher protocols had | |
| +transformed his life and the lives of those he touched, reminding | |
| +everyone that the threads of connection woven by the old internet would | |
| +forever remain intertwined with the fabric of their shared memories. | |
| diff --git a/example/uptime b/example/uptime | |
| @@ -0,0 +1,8 @@ | |
| +#!/bin/sh | |
| + | |
| +# Print the current date | |
| +echo "Current date: $(date)" | |
| + | |
| +# Print system uptime | |
| +uptime=$(uptime) | |
| +echo "System uptime: $uptime" | |
| diff --git a/go.mod b/go.mod | |
| @@ -0,0 +1,3 @@ | |
| +module fingered | |
| + | |
| +go 1.20 | |
| diff --git a/main.go b/main.go | |
| @@ -0,0 +1,95 @@ | |
| +package main | |
| + | |
| +import ( | |
| + "fmt" | |
| + "net" | |
| + "path/filepath" | |
| + "strings" | |
| + | |
| + "fingered/config" | |
| + "fingered/utils" | |
| +) | |
| + | |
| +type Config struct { | |
| + Threads int | |
| + Port int | |
| + Path string | |
| +} | |
| + | |
| +const bufferSize = 1024 | |
| + | |
| +func handleRequest(conn net.Conn, dir string, index string) { | |
| + defer conn.Close() | |
| + | |
| + // Read the incoming request | |
| + buffer := make([]byte, bufferSize) | |
| + n, err := conn.Read(buffer) | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %v", err) | |
| + return | |
| + } | |
| + | |
| + request := strings.TrimSpace(string(buffer[:n])) | |
| + | |
| + // Sanitize the request | |
| + if len(request) > 0 && !utils.IsValidWord(request) { | |
| + utils.LogMsg("INFO: Invalid username") | |
| + _, err = utils.WriteResponse(conn, "Invaild user\n") | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %s", err) | |
| + return | |
| + } | |
| + | |
| + return | |
| + } | |
| + | |
| + if len(request) == 0 { | |
| + request = index | |
| + } | |
| + | |
| + response, err := utils.GetContent(filepath.Join(dir, request)) | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %s", err) | |
| + return | |
| + } | |
| + | |
| + _, err = utils.WriteResponse(conn, response) | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %s", err) | |
| + return | |
| + } | |
| + | |
| +} | |
| + | |
| +func main() { | |
| + | |
| + cfg := config.ParseFlags() | |
| + | |
| + connectionChannel := make(chan net.Conn, cfg.Threads) | |
| + | |
| + for i := 0; i < cfg.Threads; i++ { | |
| + go func() { | |
| + for conn := range connectionChannel { | |
| + handleRequest(conn, cfg.Dir, cfg.Index) | |
| + } | |
| + }() | |
| + } | |
| + | |
| + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %v", err) | |
| + return | |
| + } | |
| + defer listener.Close() | |
| + | |
| + utils.LogMsg("Starting with threads: %d, port: %d, dir: %s", cfg.Threa… | |
| + | |
| + for { | |
| + conn, err := listener.Accept() | |
| + if err != nil { | |
| + utils.LogMsg("ERROR: %v", err) | |
| + continue | |
| + } | |
| + connectionChannel <- conn | |
| + } | |
| +} | |
| diff --git a/tests/utils_test.go b/tests/utils_test.go | |
| @@ -0,0 +1,113 @@ | |
| +package tests | |
| + | |
| +import ( | |
| + "bytes" | |
| + "log" | |
| + "os" | |
| + "strings" | |
| + "testing" | |
| + | |
| + "fingered/utils" | |
| +) | |
| + | |
| +func TestIsValidWord(t *testing.T) { | |
| + validWords := []string{ | |
| + "john", "alice", "bob", "dave", | |
| + "john123", "alice456", "dave789", | |
| + "john_doe", "alice_smith", "dave_jones", | |
| + } | |
| + | |
| + invalidWords := []string{ | |
| + "123", // Numbers only | |
| + "foo.bar", // Special characters | |
| + " ", // Empty string | |
| + "username=", // Potential injection… | |
| + "../", // Directory traversal | |
| + "/etc/passwd", // Absolute path trave… | |
| + "\\windows\\system32\\", // Windows path traver… | |
| + "ROOT", // Uppercase letters | |
| + "john_doe_", // Underscore at the e… | |
| + "john_doe_doe_doe_doe_doe_doe_doe_doe", // Too long | |
| + "foo|bar", // Shell pipe | |
| + "filename>.txt", // Shell redirection | |
| + "cmd &", // Background execution | |
| + "`ls -l`", // Command substitution | |
| + "$(echo hi)", // Command substitution | |
| + "{malicious}", // Command grouping | |
| + "evil$word", // Dollar sign | |
| + "abc;def", // Command chaining | |
| + "[evil]", // Command grouping | |
| + "nasty~word", // Tilde expansion | |
| + "question?", // Question mark | |
| + "exclamation!", // Exclamation mark | |
| + "\"quoted\"", // Double quotes | |
| + "'quoted'", // Single quotes | |
| + "escaped\\", // Backslash | |
| + } | |
| + | |
| + for _, word := range validWords { | |
| + if !utils.IsValidWord(word) { | |
| + t.Errorf("isValidWord(%s) returned false, expected tru… | |
| + } | |
| + } | |
| + | |
| + for _, word := range invalidWords { | |
| + if utils.IsValidWord(word) { | |
| + t.Errorf("isValidWord(%s) returned true, expected fals… | |
| + } | |
| + | |
| + } | |
| +} | |
| + | |
| +func TestGetContent(t *testing.T) { | |
| + // Create a temporary shell script file for testing | |
| + scriptContent := "#!/bin/sh\n\necho 'Hello, World!'" | |
| + scriptFile, err := os.CreateTemp("", "test_script_*.sh") | |
| + if err != nil { | |
| + t.Fatalf("Failed to create temporary script file: %v", err) | |
| + } | |
| + defer os.Remove(scriptFile.Name()) | |
| + defer scriptFile.Close() | |
| + | |
| + _, err = scriptFile.WriteString(scriptContent) | |
| + if err != nil { | |
| + t.Fatalf("Failed to write to temporary script file: %v", err) | |
| + } | |
| + | |
| + tests := []struct { | |
| + filePath string | |
| + expected string | |
| + }{ | |
| + {"nonexistent.txt", "file not found"}, | |
| + {scriptFile.Name(), "Hello, World!\n"}, | |
| + } | |
| + | |
| + for _, test := range tests { | |
| + actual, err := utils.GetContent(test.filePath) | |
| + if err != nil { | |
| + if actual != test.expected { | |
| + t.Errorf("For file %s, expected '%s', but got … | |
| + } | |
| + } | |
| + | |
| + if actual != test.expected { | |
| + t.Errorf("For file %s, expected '%s', but got '%s'", t… | |
| + } | |
| + } | |
| +} | |
| + | |
| +func TestLogMsg(t *testing.T) { | |
| + var buf bytes.Buffer | |
| + log.SetOutput(&buf) | |
| + defer func() { | |
| + log.SetOutput(os.Stderr) | |
| + }() | |
| + | |
| + utils.LogMsg("Test message: %s %d", "Hello", 123) | |
| + logOutput := buf.String() | |
| + expectedLogMessage := "Test message: Hello 123" | |
| + | |
| + if !strings.Contains(logOutput, expectedLogMessage) { | |
| + t.Errorf("Log message does not contain the expected message. G… | |
| + } | |
| +} | |
| diff --git a/utils/utils.go b/utils/utils.go | |
| @@ -0,0 +1,66 @@ | |
| +package utils | |
| + | |
| +import ( | |
| + "fmt" | |
| + "log" | |
| + "net" | |
| + "os" | |
| + "os/exec" | |
| + "unicode" | |
| +) | |
| + | |
| +func LogMsg(format string, args ...interface{}) { | |
| + message := fmt.Sprintf(format, args...) | |
| + log.Print(message) | |
| +} | |
| + | |
| +func GetContent(filePath string) (string, error) { | |
| + _, err := os.Stat(filePath) | |
| + if os.IsNotExist(err) { | |
| + return "file not found", err | |
| + } | |
| + | |
| + content, err := os.ReadFile(filePath) | |
| + if err != nil { | |
| + return "unable to read file", err | |
| + } | |
| + | |
| + isScript := false | |
| + if len(content) > 2 && string(content[:3]) == "#!/" { | |
| + isScript = true | |
| + } | |
| + | |
| + if isScript { | |
| + cmd := exec.Command("sh", filePath) | |
| + output, err := cmd.CombinedOutput() | |
| + if err != nil { | |
| + return "file execution failed", err | |
| + } | |
| + return string(output), nil | |
| + } | |
| + | |
| + return string(content), nil | |
| +} | |
| + | |
| +func WriteResponse(conn net.Conn, response string) (string, error) { | |
| + _, err := conn.Write([]byte(response)) | |
| + if err != nil { | |
| + return "failed to write to socket", err | |
| + } | |
| + | |
| + return "", nil | |
| +} | |
| + | |
| +func IsValidWord(input string) bool { | |
| + if len(input) < 1 || len(input) > 32 || !unicode.IsLower(rune(input[0]… | |
| + return false | |
| + } | |
| + | |
| + for _, r := range input { | |
| + if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_') { | |
| + return false | |
| + } | |
| + } | |
| + | |
| + return input[len(input)-1] != '_' | |
| +} |