File: pscan/go.mod 1 module pscan 2 3 go 1.18 File: pscan/info.txt 1 pscan [options...] [hostname/ports...] 2 3 A TCP port scanner showing all open ports. Output is lines of tab-separated 4 values (TSV), and starts with a header line, which has all column names. File: pscan/main.go 1 package main 2 3 import ( 4 "io" 5 "net" 6 "os" 7 "strconv" 8 "sync" 9 "time" 10 11 _ "embed" 12 ) 13 14 // Note: the code is avoiding using the fmt package to save hundreds of 15 // kilobytes on the resulting executable, which is a noticeable difference. 16 17 //go:embed info.txt 18 var info string 19 20 const ( 21 // maxConcurrency is how many ports can be open at once, to save memory 22 maxConcurrency = 100 23 24 // maxPort is `calculated` to avoid typo-style bugs 25 maxPort = 1<<16 - 1 26 27 // timeout gives plenty of waiting-time to check localhost ports 28 timeout = 500 * time.Millisecond 29 ) 30 31 func main() { 32 if len(os.Args) > 1 { 33 switch os.Args[1] { 34 case `-h`, `--h`, `-help`, `--help`: 35 os.Stderr.WriteString(info) 36 return 37 } 38 } 39 40 pscan(os.Stdout) 41 } 42 43 type valueWriter interface { 44 io.Writer 45 io.StringWriter 46 } 47 48 func pscan(w valueWriter) { 49 _, err := w.WriteString("date\ttime\tTCP port\n") 50 if err != nil { 51 return 52 } 53 54 // quit isn't strictly needed, as this app could just quit abruptly, but 55 // it makes the scanning funcs more general/reusable when copied verbatim 56 quit := make(chan struct{}) 57 defer close(quit) 58 59 used := make(chan int) 60 go pscanDispatch(quit, used) 61 62 for port := range used { 63 w.WriteString(time.Now().Format("2006-01-02\t15:04:05")) 64 w.WriteString("\t") 65 w.WriteString(strconv.FormatInt(int64(port), 10)) 66 _, err := w.WriteString("\n") 67 68 // assume output-errors are always due to later tools in a pipe only 69 // wanting part of this app's output, so always quit successfully and 70 // (relatively) quickly 71 if err != nil { 72 quit <- struct{}{} 73 return 74 } 75 } 76 } 77 78 // pscanDispatch simplifies control-flow for func pscan 79 func pscanDispatch(quit <-chan struct{}, used chan int) { 80 defer close(used) 81 82 // permissions limits how many ports are being checked at any time, 83 // avoiding using hundreds of megabytes in the process, while still 84 // being done checking all ports in a few seconds 85 permissions := make(chan struct{}, maxConcurrency) 86 defer close(permissions) 87 88 var tasks sync.WaitGroup 89 90 for port := 1; port <= maxPort; port++ { 91 select { 92 case <-quit: 93 tasks.Wait() 94 return 95 default: 96 // default clause avoids hanging on the quit-signaller, 97 // preventing any task from ever starting 98 } 99 100 permissions <- struct{}{} 101 tasks.Add(1) 102 103 go func(port int) { 104 defer tasks.Done() 105 defer func() { <-permissions }() 106 if checkPortTCP(port) { 107 used <- port 108 } 109 }(port) 110 } 111 112 tasks.Wait() 113 } 114 115 // checkPortTCP handles each concurrently-dispatched port-checking task 116 func checkPortTCP(port int) (inUse bool) { 117 addr := `:` + strconv.FormatInt(int64(port), 10) 118 conn, err := net.DialTimeout(`tcp`, addr, timeout) 119 if err != nil { 120 return false 121 } 122 conn.Close() 123 return true 124 } File: pscan/mit-license.txt 1 The MIT License (MIT) 2 3 Copyright © 2024 pacman64 4 5 Permission is hereby granted, free of charge, to any person obtaining a copy of 6 this software and associated documentation files (the “Software”), to deal 7 in the Software without restriction, including without limitation the rights to 8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 of the Software, and to permit persons to whom the Software is furnished to do 10 so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in all 13 copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 SOFTWARE.