Compare commits

...

49 Commits

Author SHA1 Message Date
Brendan Le Glaunec 80e75061da Create separate go module for xplatform magefile (#284) 2021-04-05 15:58:07 +02:00
Brendan Le Glaunec 5b48737cc7 Add crossplatform docker builds (#283) 2021-04-05 15:38:28 +02:00
Brendan Le Glaunec da2dac70ac Update curl-dev dependency version (#282) 2021-04-05 12:48:48 +02:00
Sliicy 8c8ea1209b Update routes with support for Lorex LHB927 (#270)
The Lorex LHB927 supports RTSP streams which have routes:
ch1/0 for camera 1
ch2/0 for camera 2
...
They start counting from 1, not 0.
Alternatively, they can also be reached at:
ch1_0
ch2_0
...
Adding more routes (for cameras #3 & 4) increases the odds of finding the DVR, if for example, camera 1 is offline.
2020-08-13 09:48:43 +02:00
Brendan Le Glaunec df3718a06c Remove /v5 in module path in documentation 2020-08-12 09:10:29 +02:00
Ullaakut 6486d04e61 Test debug mode in attack & remove unnecessary aliases & newlines 2020-05-04 11:10:48 +02:00
Ullaakut 96928ac43c Set module version to v5
* Update README to reflect changes
* Various README improvements
2020-05-04 11:10:48 +02:00
Ullaakut 8e7de3f59e Support multi-route detection
* Change stream model to support multiple routes
* Simplify attack algorithm
* Use dummy route to detect cameras which accept all routes
2020-05-04 11:10:48 +02:00
Ullaakut fbc0b7a66d Move issue template to .github and remove old changelog file 2020-05-04 11:10:48 +02:00
Ullaakut 78eda6672e Fix stream discovery on non-standard ports by using service info mode in nmap scan 2020-05-04 11:10:48 +02:00
Ullaakut 9f05634531 Reword error message when no stream is found 2020-05-04 11:10:48 +02:00
Ullaakut defc308a9d Remove unnecessary import alias 2020-05-04 11:10:48 +02:00
Brendan Le Glaunec 9a6c030a74 Remove spam from curl verbose mode (#257) 2020-01-21 08:49:36 +01:00
Brendan Le Glaunec afe2caddd6 Add routes and credentials for Besder IP cameras (#256) 2020-01-21 08:39:06 +01:00
Brendan Le Glaunec 8349bc7c3a Fix scan unit tests (#253) 2020-01-17 09:47:38 +01:00
Brendan Le Glaunec 04ab1cfc8d Use older curl version to fix digest authentication (#252) 2020-01-17 09:18:22 +01:00
Brendan Le Glaunec d233fd850e Update nmap dependency to v2.0.0 (#250) 2019-11-30 16:57:27 +01:00
Brendan Le Glaunec 948aca316b Add libcurl requirement to build from binary (#249) 2019-11-30 16:23:14 +01:00
Brendan Le Glaunec 3f05737bf2 Fix scan interval option (#246) 2019-11-16 09:46:09 +01:00
Brendan Le Glaunec 4aabf47a5d Add scan interval option (#245)
* Add scan interval option

* Update tests for scan interval

* Handle missing target properly

* Update documentation to reflect that durations are not in milliseconds
2019-11-11 21:42:38 +01:00
Brendan Le Glaunec cb47aef7e4 Fix capitalization issues in import path (#244) 2019-11-11 20:17:39 +01:00
Ullaakut bb05fcff6f Update funding 2019-10-18 07:55:17 +02:00
Brendan Le Glaunec 8634ba84ca Update dictionaries & dependencies for dictionary fetch tool (#240) 2019-10-03 22:04:17 +02:00
Brendan Le Glaunec 3bd6b9171e Encourage people to donate to charities 2019-07-02 09:37:26 +02:00
Brendan Le Glaunec 18a933ba45 Remove goreleaser (#232)
* Debug goreleaser

* Remove goreleaser
2019-06-29 04:27:57 +02:00
Ullaakut 0187d9a553 Update dependencies 2019-06-29 04:23:01 +02:00
Brendan Le Glaunec 7672da5b6d Update dependencies for release 4.1.0 (#231)
* Update dependencies for release 4.1.0

* Update packages in travis build
2019-06-29 03:03:17 +02:00
Brendan Le Glaunec 966266f742 Fix erroneous build instructions (#230) 2019-06-27 07:23:52 +02:00
Brendan Le Glaunec 0738d08966 Fix goreleaser deployment (#228) 2019-06-15 21:42:14 +02:00
Brendan Le Glaunec 4603096b93 Use os.ExpandEnv for injecting gopath in dict path (#227) 2019-06-15 18:36:35 +02:00
Brendan Le Glaunec a5d3455333 Add Goreleaser (#226) 2019-06-15 13:52:56 +02:00
Brendan Le Glaunec c83f961ffc Change curl errors from debug to error level (#223) 2019-06-15 08:06:48 +02:00
Brendan Le Glaunec a6ed312eaf Update second occurrence of build instructions (#220) 2019-06-11 09:07:38 +02:00
Brendan Le Glaunec 35b0cf26d9 Improve documentation and add warning when no gopath (#219)
* Improve documentation

* Add warning when no GOPATH set with default dictionaries
2019-06-11 09:02:24 +02:00
Brendan Le Glaunec 0f011a1797 Fix attack loop and add reolink routes (#215) 2019-06-06 10:04:55 +02:00
Brendan Le Glaunec 7719110f1e Remove changelog in favor of github release page 2019-05-27 01:57:19 +02:00
Brendan Le Glaunec 212ac2f0d5 Cameradar v4 (#212)
* Refactor of cameradar library

* Old unit tests updated & improved. New unit tests inc

* Update documentation & issue template

* Update dependencies

* Update TravisCI build script to reflect argument change

* Remove outdated contributing guide

* Update README with more examples and remove part on library

* Add second camera to Travis build script & improve error detection

* Fix typo in travis script & add missing image to readme

* Remember that travis uses bash syntax not fish

* Use relative paths for images in the README
2019-05-26 08:33:08 +02:00
Brendan Le Glaunec 2e49587cc2 Remove erroneous route and update dictionary updater (#207)
* Remove erroneous route and update dictionary updater

* Rename dictionary updater
2019-05-23 09:19:04 +02:00
Brendan Le Glaunec 47285675b9 Add route dictionary generator (#204) 2019-05-23 01:42:04 +02:00
Brendan Le Glaunec 2678df2e4c Add default credentials for aiphone cameras (#203) 2019-05-22 22:25:32 +02:00
Ullaakut 862e9f3de9 Add unit tests for DetectAuthType function 2019-05-22 22:14:36 +02:00
Ullaakut 260a9645be Add digest authentication support 2019-05-22 22:14:36 +02:00
Brendan Le Glaunec 1d5d606085 Update documentation on new dependency management (#200) 2019-05-21 07:31:18 +02:00
Brendan LE GLAUNEC c249be1cc0 Update documentation on new dependency management (#196) 2019-05-11 16:48:37 +02:00
Brendan LE GLAUNEC 1ec3a5fe44 Update documentation on new dependency management (#195) 2019-05-11 16:33:16 +02:00
Ullaakut 3b082ea736 Update to disgo 0.3.0 2019-04-06 12:56:15 +02:00
Brendan LE GLAUNEC b6ebd468c6 Integrate Disgo for user interface (#193) 2019-03-10 20:12:46 +01:00
Brendan LE GLAUNEC ceb210f281 Switch to go modules, use forked go-curl to fix CI (#192) 2019-03-10 19:14:11 +01:00
Brendan LE GLAUNEC fcb627dccd Update Readme with new dependencies and update copyright date (#191) 2019-03-10 17:16:39 +01:00
993 changed files with 2610 additions and 403166 deletions
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ullaakut] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -24,6 +24,7 @@ Please select one:
## Environment
My operating system:
- [ ] Windows
- [ ] OSX
- [ ] Linux
@@ -34,17 +35,17 @@ OS architecture: <architecture>
## Issue
### What was expected?
### What was expected
<expected behavior>
### What happened?
### What happened
<observed behavior>
### Logs
If your issue is with Cameradar's binary or docker image, please run it with `-l` to print logs, and paste them here:
If your issue is with Cameradar's binary or docker image, please run it with `-v` to print verbose logs, and paste them here:
```
<cameradar logs>
+3
View File
@@ -5,3 +5,6 @@
# Golang
/bin/*
/pkg/*
# Builds
dist/
+43
View File
@@ -0,0 +1,43 @@
project_name: cameradar
dist: dist/cameradar
env:
- GO111MODULE=on
before:
hooks:
- go mod download
builds:
- binary: cameradar
main: ./cmd/cameradar
goos:
- windows
- darwin
- linux
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 6
- 7
ignore:
- goos: darwin
goarch: 386
changelog:
skip: true
checksum:
name_template: "{{ .ProjectName }}_checksums.txt"
archives:
- name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ if .Arm}}v{{ .Arm }}{{ end }}"
format: tar.gz
format_overrides:
- goos: windows
format: zip
files:
- CHANGELOG.md
+29 -19
View File
@@ -2,11 +2,21 @@ dist: trusty
sudo: required
language: go
addons:
apt:
packages:
# needed for the nfpm pipe:
- rpm
# needed for the snap pipe:
- snapd
env:
- DEP_VERSION="0.5.0"
- GO111MODULE=on
# needed for the snap pipe:
- PATH=/snap/bin:$PATH
services:
- docker
- docker
before_install:
- echo "Testing Docker Hub credentials"
@@ -18,34 +28,34 @@ before_install:
- sudo apt-get remove docker docker-engine docker.io
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo apt-get update
- sudo apt-get install -y docker-ce nmap
- sudo apt-get install -y docker-ce nmap libcurl4-openssl-dev
- go get github.com/mattn/goveralls
- docker version
- curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
- chmod +x $GOPATH/bin/dep
install:
- dep ensure
- docker build -t cameradar .
script:
# Run unit tests
- go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
# Launch a fake camera to check if cameradar is able to access it
- docker run -d --name=fake_camera -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -p 8554:8554 ullaakut/rtspatt
- GO111MODULE=on go test -v -covermode=count -coverprofile=coverage.out
- GO111MODULE=on $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
# Launch fake cameras to check if cameradar is able to access them
- docker run -d --name="fake_camera_digest" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="admin" -e RTSP_PASSWORD="12345" -e RTSP_AUTHENTICATION_METHOD="digest" -p 8554:8554 ullaakut/rtspatt
- docker run -d --name="fake_camera_basic" -e RTSP_ROUTE="/live.sdp" -e RTSP_USERNAME="root" -e RTSP_PASSWORD="root" -e RTSP_AUTHENTICATION_METHOD="digest" -p 5554:5554 ullaakut/rtspatt
# Launch cameradar on the local machine
- docker run --net=host -t cameradar -t 0.0.0.0 -l > logs.txt
- docker logs fake_camera > camera_logs.txt
# Stop the fake camera
- docker stop fake_camera
- docker run --net=host -t cameradar -t 0.0.0.0 -p 8554,5554 -v > logs.txt
# Gather the logs from the cameras
- docker logs fake_camera_digest > camera_digest_logs.txt
- docker logs fake_camera_basic > camera_basic_logs.txt
# Stop the fake cameras
- docker stop fake_camera_basic
- docker stop fake_camera_digest
# Print logs
- cat camera_logs.txt
- cat camera_digest_logs.txt
- cat camera_basic_logs.txt
- cat logs.txt
# check if file contains more than one line
# 1 line: Error message because no streams were found
# More lines: Logs for all found cameras
- if [[ $(wc -l <logs.txt) -lt 2 ]]; then exit 1; fi
- grep "Successful attack" logs.txt || exit 1
- git clean -fd
notifications:
email:
-222
View File
@@ -1,222 +0,0 @@
# Cameradar Changelog
This file lists all versions of the repository and precises all changes.
## v2.0.0
#### Major changes:
* Cameradar is no longer a C++ application but a Golang library
* It is also a Golang application replacing the former C++ one (the C++ Cameradar image can still be used with the tag `1.1.4`)
* The new docker image is twice lighter (14MB vs 379MB before)
* The Cameradar golang library enables users to build their own application around camera discovery and attack. Example of applications could be an automatic camera discovery daemon with scheduled scans, a security audit tool to check if CCTV cameras are protected from attacks by being isolated and having strong passwords, etc.
## v1.1.4
#### Minor changes:
* Simplified use of Docker image
* Renamed MySQL table name to be more explicit
* Refactoring of the Golang functional tester done
* The output was made more human readable
* Added automatic code quality checks for pull requests
* Added contribution documentation
* Updated dictionaries to add user suggestions for Chinese cameras
* Enhanced `result.json` file's format
#### Bugfixes:
* Fixed a bug in the functional testing in which if the `result.json` file was not formatted correctly, the test failed but was still considered a success.
## v1.1.3
#### Minor changes:
* Added automatic pushes to DockerHub to the travis integration
* Made travis configuration file better
* Changed the package generation scripts to make them report errors
* Removed old etix_rtsp_server binary from the test folder
#### Bugfixes:
* Fixed an issue that made it mandatory to launch tests at least once so that they can work the second time
* Fixed an issue that made the golang testing tool not compile in the testing script
* Fixed an issue that made the golang testing tool sometimes ignore some tests
* The previous known issue has been investigated and we don't know where it came from. However after a night of testing I have been unable to reproduce it, so I will consider it closed
## v1.1.2
#### Minor changes:
* Added travis integration
* Added default environment value for Docker deployment
* Updated docker image description with new easy usage
* Updated README badges style (replaced flat with square-flat)
* Build last package can now also generate a debug package if given the `Debug` command-line argument
#### Known issues
* There is still the issue with Camera Emulation Server, see the [previous version's patchnote](#v1.1.1) for more information.
## v1.1.1
#### Minor changes:
* Removed unnecessary null pointer checks (thanks to https://github.com/elfring)
* Updated package description
* Removed debug message in CMake build
* Added `/ch01.264` to the URL dictionary in the deployment (Comelit default RTSP URL)
* Updated tests partially (still needs work to make the code cleaner)
* Variable names are now compliant with Golang best practices
* JSON variable names are back to normal
* Functions have been moved in more appropriate source files
* Structure definitions have been moved in more appropriate source files
* Source files have been renamed to be more relevant
* JUnit output now considers each camera as a test case
* JUnit output now contains errors which makes debugging much easier
* Added header files where it was forgotten
#### Bugfixes:
* Fixed an issue where if you loose your internet connection during thumbnail generation, FFMpeg would get stuck forever and thus Cameradar would never finish
* Fixed an issue where multithreading could cause crashes
* Fixed an issue where the routes dictionary was mistaken for the credentials dictionary
* Fixed issues with the golang testing tool
* Fixed automated camera generation
* Fixed docker IP address resolution
#### Known issues:
* There is an issue with Camera Emulation Server that makes it impossible for Cameradar to generate thumbnails, which is why right now the verification of the thumbnails presence is commented and it is assumed correct. It is probably an issue with GST-RTSP-Server but requires investigation.
## v1.1.0
#### Major changes:
* There are more command line options
* Port can now be overridden in the command line
* Target can now be overridden in the command line
* Bruteforce is now multithreaded and will use as many threads as there are discovered cameras
* Thumbnail generation is now multithreaded and will use as many threads as there are discovered cameras
* There are now default configuration values in order to make cameradar easier to use
#### Minor changes:
* The algorithms take external input into account (so that a 3rd party can change the DB to help Cameradar in real-time) and thus check the persistent data at each iteration
* The default log level is now DEBUG instead of INFO
* The attack logs are now INFO instead of DEBUG
* The thumbnail generation logs are now INFO instead of DEBUG
#### Bugs fixed
* Fixed a bug in which the MySQL cache manager would consider a camera with known ids as having a valid path even if it weren't
* Fixed a bug in which TCP RTSP streams would not generate thumbnails
## v1.0.5
* Fixed error in MySQL Cache Manager in which thumbnail generation on valid streams could not be done
* Fixed potential crash in the case the machine running cameradar has no memory left to allocate space for the dynamic cache manager
## v1.0.4
#### Bugs fixed:
* Fixed nmap package detection
## v1.0.3
#### Bugs fixed:
* Corrected GStreamer check
## v1.0.2
#### Bugs fixed:
* Fixed issues in MySQL Cache Manager
#### Minor changes:
* Added useful debug logs
## v1.0.1
### Ubuntu 16.04 Release
#### Major changes:
* The Docker deployment is now done using Ubuntu 16.04 instead of Ubuntu 15.10, so that it uses more recent packages.
#### Minor changes:
* Removed useless dependencies
## v1.0.0
### First production-ready release
#### Major changes:
* Added functional testing
## v0.2.2
After doing some testing on a weirdly configured camera network in a far away Datacenter, I discovered that some Cameras needed a few tweaks to the Cameradar attack method in order to be accessed.
#### Major changes:
* Cameradar can access Cameras that are configured to always send 400 Bad Requests responses
#### Minor changes:
* Changed iterator name from `it` to `stream` in dumb cache manager to improve code readability
#### Bugfixes:
* Cameradar no longer considers a timing out Camera as an accessible stream
## v0.2.1
This package adds fixes the Docker deployment package.
#### Minor changes
* Fixed the Docker deployment package
* Updated README
## v0.2.0
### MySQL Cache Manager Release
This package adds a new cache manager using a MySQL database, that can store the results between mutiple uses.
#### Major changes
* Added a MySQL Cache Manager
#### Minor changes
* Removed legacy code
* Removed boost dependency
* Improved debugging logs
## v0.1.1
### Docker release
This package adds a way to deploy Cameradar using Docker.
#### Major changes
* Added a quick Docker deployment process
* Added automatic dependencies downloading through CMake for the manual installation
* Added CPack packaging for the Docker deployment
#### Minor changes
* Changed recommended cloning method to HTTPS
* Added lots of informations to README.md
## v0.1.0
This package was the first OpenSource version of Cameradar. It contained only a simple cache manager and had some bugs.
-82
View File
@@ -1,82 +0,0 @@
# Cameradar Contribution
This file will give you guidelines on how to contribute if you want to, and will list known contributors to this repo.
If you're not into software development or not into Golang, you can still help. Updating the dictionaries for example, would be a really cool contribution! Just make sure the credentials and routes you add are **default constructor credentials** and not custom credentials.
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) or to directly [create an issue](https://github.com/Ullaakut/cameradar/issues)!
## Version 2.0.0
*Cameradar* is the name of the Golang library and the binary that serves as an example of its use, as well as the docker image that runs the binary.
The 2.0.0 version was a complete refactorring of the Cameradar C++ tool, which came from the fact that most users who want to access cameras either wanted to launch it with the basic cache manager, mostly using the docker image already provided in this repository, or did not use it because it did not integrate into their software solution easily.
Transforming it into a library allowed developers to use it directly in their own code exactly as they want, allowing for a greater flexibility. The Cameradar binary also provides a simple use example as well as maintains the old simple way of using Cameradar for non-developers.
## Workflow
### Branches & issues
If you want to work on an issue, make sure you create a specific branch for this issue using the format `issue_number-solution_explanation`. Examples are:
If issue `#64` is `Improve network scan performance`, the branch to fix it should be something like: `64-improve-network-scan-performance`. Note that it should always start with a verb conjugated in the infinitive form, and describe what the commits's effects will be on the codebase. One branch should only be for one change. If your branch fixes multiple things, you're doing it wrong.
Always make sure you're not working on the same issue as someone else, by asking on the issue thread to be assigned to it.
### Commit names
The name of the commits should always be #[issue number] [effect of the issue] (ex: `#343 Improve test coverage`).
When working on your local branch, you can do as many commits as you want, obviously. The most important is that you squash your commits before creating your pull request, or at least before it is merged.
In case you're not familiar with squashing, here is a simple way to do it :
- `git fetch origin` will make sure that you have a local version of the origin repository that is up to date (will not overwrite anything on your branch, no worries)
- `git rebase -i origin/master` will start the process of rebasing your branch
- This will open a file letting you decide what to do with the commits. You want to keep the first `pick` and write `s` or `squash` instead of `pick` for all other commits below.
- If there are conflicts, you will fix them step by step by following what git tells you, it's pretty straight-forward.
- If there are no conflicts or if they are resolved, git will let you edit the commit names. Don't forget to comment the commit names of the commits you squashed if they are not relevant by adding a # character in front of the commit message, and make sure that the commit message you left follows the aforementioned guidelines.
- Now run `git log`, you should see only one commit by the name you chose during the rebase.
- You can now `git push -f` if you already pused your branch on origin or simply push without the `-f` if it's your first push on origin. The reason for the `-f` is that when you squash your commits, you create a new one that will conflict with the state of your branch on origin. If you pull, it will overwrite your local state, so don't do that except if you messed up your rebase.
### Pull Requests
When your pull request is created, GitHub will first check for conflicts, Codacy will check the shell and C++ code's quality and then Travis CI will try to build and launch functional tests of your versions of Cameradar.
If GitHub reports conflicts with the develop branch, you should resolve them by yourself using your git command-line interface. The easiest and cleanest way is to use `git rebase -i origin/develop` and follow git's instructions.
If Codacy reports new issues, they will be added in the comments of the PR to let you know what you should fix.
If Travis CI reports errors, you should be able to view the logs [by clicking here](https://travis-ci.org/Ullaakut/cameradar/builds) and you should fix it. No PR will be merged before all tests are passing correctly.
When creating your pull request, our hooks will make sure that your code:
- Builds
- Has 100% passing unit tests
- Can actually access a camera using a functional test
- Still has equivalent or higher test coverage (using coveralls)
Make sure to write in the PR description what issue it fixes. GitHub will intepret it and automatically close the issue once your pull request is closed. Just write Fixes #IssueNumber in the description.
When your pull request is created, GitHub will first check for conflicts and then your code will be reviewed by the maintainers of this repository.
If GitHub reports conflicts with the `master` branch, you should resolve them by yourself using your git command-line interface. The easiest and cleanest way is to use `git rebase -i origin/master` and follow git's instructions. If we report issues with your code, you should resolve them and then ping the person that reported them to notify them that you did the requested changes.
Once everything is in order, we will merge your pull request.
### Coding guidelines
Your code should just
- Not decrease the results of Cameradar on https://goreportcard.com/report/github.com/Ullaakut/cameradar
- Pass the code review
#### Golang
- All Golang code has to be formated using `gofmt` or `goreturns`.
- Make sure you follow the Golang [best practices](https://golang.org/doc/effective_go.html)
## Contributors
- **Brendan Le Glaunec** - [@Ullaakut](https://github.com/Ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
- **Jeremy Letang** - [@jeremyletang](https://github.com/jeremyletang) - letang.jeremy@gmail.com : *Idea of the project & Mentorship*
- **ishanjain28** - [@ishanjain28](https://github.com/ishanjain28) - ishanjain28@gmail.com : *Implemented the environment variables support*
+9 -7
View File
@@ -2,7 +2,7 @@
FROM golang:alpine AS build-env
COPY . /go/src/github.com/Ullaakut/cameradar
WORKDIR /go/src/github.com/Ullaakut/cameradar/cameradar
WORKDIR /go/src/github.com/Ullaakut/cameradar/cmd/cameradar
RUN apk update && \
apk upgrade && \
@@ -12,23 +12,25 @@ RUN apk update && \
libc-dev \
git \
pkgconfig
ENV DEP_VERSION="0.5.0"
RUN curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o $GOPATH/bin/dep
RUN chmod +x $GOPATH/bin/dep
RUN dep ensure
ENV GO111MODULE=on
RUN go version
RUN go build -o cameradar
# Final stage
FROM alpine
# Necessary to install curl v7.64.0-r3.
# Fix for https://github.com/Ullaakut/cameradar/issues/247
RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >> /etc/apk/repositories
RUN apk --update add --no-cache nmap \
nmap-nselibs \
nmap-scripts \
curl-dev
curl-dev==7.64.0-r5
WORKDIR /app/cameradar
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/dictionaries/ /app/dictionaries/
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cameradar/ /app/cameradar/
COPY --from=build-env /go/src/github.com/Ullaakut/cameradar/cmd/cameradar/ /app/cameradar/
ENV CAMERADAR_CUSTOM_ROUTES="/app/dictionaries/routes"
ENV CAMERADAR_CUSTOM_CREDENTIALS="/app/dictionaries/credentials.json"
Generated
-300
View File
@@ -1,300 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:50b257fde8ffe647a6fdb6a7fb1314711232b7ecfb891f5cd5c140c046ab89cd"
name = "github.com/Ullaakut/nmap"
packages = [
".",
"pkg/osfamilies",
]
pruneopts = "UT"
revision = "6d3f7b465d7b37c8e019c95bc1c9f6c4f93e4ee7"
[[projects]]
branch = "master"
digest = "1:e339fa18a6e1d4dd04282f5ba77b8a09cbbf1067ba20915de87d1e65367a9c9f"
name = "github.com/andelf/go-curl"
packages = ["."]
pruneopts = "UT"
revision = "9d81ad32de98e80df412c890c75d8964f696a910"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:865079840386857c809b72ce300be7580cb50d3d3129ce11bf9aa6ca2bc1934a"
name = "github.com/fatih/color"
packages = ["."]
pruneopts = "UT"
revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4"
version = "v1.7.0"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "UT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
branch = "master"
digest = "1:842de8d5a4c8fdbbceb55ab398dd8b68a35fe7f322012cf70571baa35c333ffa"
name = "github.com/gernest/wow"
packages = [
".",
"spin",
]
pruneopts = "UT"
revision = "7e0b2a2398989a5d220eebac5742d45422ba7de8"
[[projects]]
digest = "1:e1ff887e232b2d8f4f7c7db15a5fac7be418025afc4dda53c59c765dbb5aa6b4"
name = "github.com/go-playground/locales"
packages = [
".",
"currency",
]
pruneopts = "UT"
revision = "f63010822830b6fe52288ee52d5a1151088ce039"
version = "v0.12.1"
[[projects]]
digest = "1:e022cf244bcac1b6ef933f1a2e0adcf6a6dfd7b872d8d41e4d4179bb09a87cbc"
name = "github.com/go-playground/universal-translator"
packages = ["."]
pruneopts = "UT"
revision = "b32fa301c9fe55953584134cb6853a13c87ec0a1"
version = "v0.16.0"
[[projects]]
digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = "UT"
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
version = "v1.0.0"
[[projects]]
digest = "1:6782ffc812e8e700e6952ede1e60487ff1fd9da489eff762985be662a7cfc431"
name = "github.com/leodido/go-urn"
packages = ["."]
pruneopts = "UT"
revision = "70078a794e8ea4b497ba7c19a78cd60f90ccf0f4"
version = "v1.1.0"
[[projects]]
digest = "1:2d0b44ee6208d256e29f55764db1fa41e3ae33fac6cef138acae6a79c3bd748e"
name = "github.com/magefile/mage"
packages = [
"mg",
"sh",
]
pruneopts = "UT"
revision = "aedfce64c122eef47009b7f80c9771044753215d"
version = "v1.8.0"
[[projects]]
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
name = "github.com/magiconair/properties"
packages = ["."]
pruneopts = "UT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
name = "github.com/mattn/go-colorable"
packages = ["."]
pruneopts = "UT"
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5"
name = "github.com/mattn/go-isatty"
packages = ["."]
pruneopts = "UT"
revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c"
version = "v0.0.4"
[[projects]]
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:d707dbc1330c0ed177d4642d6ae102d5e2c847ebd0eb84562d0dc4f024531cfc"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "a5d6946387efe7d64d09dcba68cdd523dc1273a3"
version = "v1.2.0"
[[projects]]
digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
version = "v1.3.0"
[[projects]]
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
version = "v1.0.0"
[[projects]]
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
version = "v1.0.3"
[[projects]]
digest = "1:de37e343c64582d7026bf8ab6ac5b22a72eac54f3a57020db31524affed9f423"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "UT"
revision = "6d33b5a963d922d182c91e8a1c88d81fd150cfd4"
version = "v1.3.1"
[[projects]]
digest = "1:ac83cf90d08b63ad5f7e020ef480d319ae890c208f8524622a2f3136e2686b02"
name = "github.com/stretchr/objx"
packages = ["."]
pruneopts = "UT"
revision = "477a77ecc69700c7cdeb1fa9e129548e1c1c393c"
version = "v0.1.1"
[[projects]]
digest = "1:15a4a7e5afac3cea801fa24831fce3bf3b5bd3620cbf8355a07b7dbf06877883"
name = "github.com/stretchr/testify"
packages = [
"assert",
"mock",
]
pruneopts = "UT"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
branch = "master"
digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = "UT"
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
[[projects]]
branch = "master"
digest = "1:3d5e79e10549fd9119cbefd614b6d351ef5bd0be2f2b103a4199788e784cbc68"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "UT"
revision = "b4a75ba826a64a70990f11a225237acd6ef35c9f"
[[projects]]
digest = "1:8029e9743749d4be5bc9f7d42ea1659471767860f0cdc34d37c3111bd308a295"
name = "golang.org/x/text"
packages = [
"internal/gen",
"internal/triegen",
"internal/ucd",
"transform",
"unicode/cldr",
"unicode/norm",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
digest = "1:8abc978192c2fca4d6a74260c3a8f4aa34d25bd3c548c938745b91b8b20cda1d"
name = "gopkg.in/go-playground/validator.v9"
packages = ["."]
pruneopts = "UT"
revision = "0277b12d53df79c9dbf7311cb07fa9c81ed621bb"
version = "v9.24.0"
[[projects]]
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
version = "v2.2.2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/Ullaakut/cameradar",
"github.com/Ullaakut/nmap",
"github.com/andelf/go-curl",
"github.com/fatih/color",
"github.com/gernest/wow",
"github.com/gernest/wow/spin",
"github.com/pkg/errors",
"github.com/spf13/pflag",
"github.com/spf13/viper",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/mock",
"gopkg.in/go-playground/validator.v9",
]
solver-name = "gps-cdcl"
solver-version = 1
-54
View File
@@ -1,54 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/andelf/go-curl"
[[constraint]]
name = "github.com/fatih/color"
version = "1.7.0"
[[constraint]]
branch = "master"
name = "github.com/gernest/wow"
[[constraint]]
name = "github.com/spf13/pflag"
version = "1.0.3"
[[constraint]]
name = "github.com/spf13/viper"
version = "1.3.1"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.2"
[prune]
go-tests = true
unused-packages = true
+65 -103
View File
@@ -1,7 +1,7 @@
# Cameradar
<p align="center">
<img src="https://raw.githubusercontent.com/Ullaakut/cameradar/master/images/Cameradar.gif" width="100%"/>
<img src="images/Cameradar.gif" width="100%"/>
</p>
<p align="center">
@@ -17,17 +17,17 @@
<a href='https://coveralls.io/github/Ullaakut/cameradar?branch=master'>
<img src='https://coveralls.io/repos/github/Ullaakut/cameradar/badge.svg?branch=master' alt='Coverage Status' />
</a>
<a href="https://golangci.com/r/github.com/Ullaakut/cameradar">
<img src="https://golangci.com/badges/github.com/Ullaakut/cameradar.svg" />
<a href="https://golangci.com/r/github.com/ullaakut/cameradar">
<img src="https://golangci.com/badges/github.com/ullaakut/cameradar.svg" />
</a>
<a href="https://goreportcard.com/report/github.com/Ullaakut/cameradar">
<img src="https://goreportcard.com/badge/github.com/Ullaakut/cameradar" />
<a href="https://goreportcard.com/report/github.com/ullaakut/cameradar">
<img src="https://goreportcard.com/badge/github.com/ullaakut/cameradar" />
</a>
<a href="https://github.com/Ullaakut/cameradar/releases/latest">
<a href="https://github.com/ullaakut/cameradar/releases/latest">
<img src="https://img.shields.io/github/release/Ullaakut/cameradar.svg?style=flat" />
</a>
<a href="https://godoc.org/github.com/Ullaakut/cameradar">
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" />
<a href="https://godoc.org/github.com/ullaakut/cameradar">
<img src="https://godoc.org/github.com/ullaakut/cameradar?status.svg" />
</a>
</p>
@@ -41,7 +41,7 @@
* Launch automated dictionary attacks to get the **username and password** of the cameras
* Retrieve a complete and user-friendly report of the results
<p align="center"><img src="https://raw.githubusercontent.com/Ullaakut/cameradar/master/images/Cameradar.png" width="250"/></p>
<p align="center"><img src="images/Cameradar.png" width="250"/></p>
## Table of content
@@ -49,13 +49,15 @@
* [Configuration](#configuration)
* [Output](#output)
* [Check camera access](#check-camera-access)
* [Command line options](#command-line-options)
* [Command-line options](#command-line-options)
* [Contribution](#contribution)
* [Frequently Asked Questions](#frequently-asked-questions)
* [License](#license)
## Docker Image for Cameradar
<p align="center"><img src="images/CameradarV4.png" width="70%"/></p>
Install [docker](https://docs.docker.com/engine/installation/) on your machine, and run the following command:
```bash
@@ -64,7 +66,7 @@ docker run -t ullaakut/cameradar -t <target> <other command-line options>
[See command-line options](#command-line-options).
e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24 -l` will scan the ports 554, 5554 and 8554 of hosts on the 192.168.100.0/24 subnetwork and attack the discovered RTSP streams and will output debug logs.
e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24` will scan the ports 554, 5554 and 8554 of hosts on the 192.168.100.0/24 subnetwork and attack the discovered RTSP streams and will output debug logs.
* `YOUR_TARGET` can be a subnet (e.g.: `172.16.100.0/24`), an IP (e.g.: `172.16.100.10`), or a range of IPs (e.g.: `172.16.100.10-20`).
* If you want to get the precise results of the nmap scan in the form of an XML file, you can add `-v /your/path:/tmp/cameradar_scan.xml` to the docker run command, before `ullaakut/cameradar`.
@@ -74,78 +76,30 @@ e.g.: `docker run -t ullaakut/cameradar -t 192.168.100.0/24 -l` will scan the po
Only use this solution if for some reason using docker is not an option for you or if you want to locally build Cameradar on your machine.
**WARNING**: Manually building the binary will **NOT WORK** for any camera that uses **DIGEST AUTHENTICATION** [if your version of `curl` is over `7.64.0`](https://github.com/Ullaakut/cameradar/pull/252), which is most likely the case. For more information, see [this response on the subject from the author of curl](https://stackoverflow.com/a/59778142/4145098).
### Dependencies
* `go`
* `dep`
#### Installing dep
* OSX: `brew install dep` and `brew upgrade dep`
* Others: Download the release package for your OS [here](https://github.com/golang/dep/releases)
* `go` (> `1.10`)
* `libcurl` development library (**[version has to be <7.66.0](https://github.com/Ullaakut/cameradar/issues/247)**)
* For apt users: `apt install libcurl4-openssl-dev`
### Steps to install
Make sure you installed the dependencies mentionned above.
1. `go get github.com/Ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
3. `dep ensure`
4. `cd cameradar`
5. `go install`
3. `cd cmd/cameradar`
4. `go install`
The `cameradar` binary is now in your `$GOPATH/bin` ready to be used. See command line options [here](#command-line-options).
## Library
### Dependencies of the library
* `curl-dev` / `libcurl` (depending on your OS)
* `nmap`
* `github.com/pkg/errors`
* `gopkg.in/go-playground/validator.v9`
* `github.com/andelf/go-curl`
#### Installing the library
`go get github.com/Ullaakut/cameradar`
After this command, the _cameradar_ library is ready to use. Its source will be in:
$GOPATH/src/pkg/github.com/Ullaakut/cameradar
You can use `go get -u` to update the package.
Here is an overview of the exposed functions of this library:
#### Discovery
You can use the cameradar library for simple discovery purposes if you don't need to access the cameras but just to be aware of their existence.
<p align="center"><img width="90%" src="https://raw.githubusercontent.com/Ullaakut/cameradar/master/images/NmapPresets.png"/></p>
This describes the nmap time presets. You can pass a value between 1 and 5 as described in this table, to the NmapRun function.
#### Attack
If you already know which hosts and ports you want to attack, you can also skip the discovery part and use directly the attack functions. The attack functions also take a timeout value as a parameter.
#### Data models
Here are the different data models useful to use the exposed functions of the cameradar library.
<p align="center"><img width="60%" src="https://raw.githubusercontent.com/Ullaakut/cameradar/master/images/Models.png"/></p>
#### Dictionary loaders
The cameradar library also provides two functions that take file paths as inputs and return the appropriate data models filled.
## Configuration
The **RTSP port used for most cameras is 554**, so you should probably specify 554 as one of the ports you scan. Not specifying any ports to the cameradar application will scan the 554, 5554 and 8554 ports.
`docker run -t --net=host ullaakut/cameradar -p "18554,19000-19010" -t localhost` will scan the ports 18554, and the range of ports between 19000 and 19010 on localhost.
`docker run -t --net=host ullaakut/cameradar -p "18554,19000-19010" -t localhost` will scan the ports `18554`, and the range of ports between `19000` and `19010` on `localhost`.
You **can use your own files for the ids and routes dictionaries** used to attack the cameras, but the Cameradar repository already gives you a good base that works with most cameras, in the `/dictionaries` folder.
You **can use your own files for the credentials and routes dictionaries** used to attack the cameras, but the Cameradar repository already gives you a good base that works with most cameras, in the `/dictionaries` folder.
```bash
docker run -t -v /my/folder/with/dictionaries:/tmp/dictionaries \
@@ -159,27 +113,27 @@ This will put the contents of your folder containing dictionaries in the docker
## Check camera access
If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able to use the GUI or the command-line to connect to the RTSP stream using this format : `rtsp://username:password@address:port/route`
If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able to use the GUI or the command-line to connect to the RTSP stream using this format: `rtsp://username:password@address:port/route`
With the above result, the RTSP URL would be `rtsp://admin:12345@173.16.100.45:554/live.sdp`
## Command line options
## Command-line options
* **"-t, --targets"**: Set target. Required. Target can be a file (see [instructions on how to format the file](#format-input-file)), an IP, an IP range, a subnetwork, or a combination of those. Example: `--targets="192.168.1.72,192.168.1.74"`
* **"-p, --ports"**: (Default: `554,5554,8554`) Set custom ports.
* **"-s, --speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. You might also want to keep it low to keep your discovery stealthy. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
* **"-T, --timeout"**: (Default: `2000`) Set custom timeout value in miliseconds after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on very performant and reliable networks.
* **"-s, --scan-speed"**: (Default: `4`) Set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. You might also want to keep it low to keep your discovery stealthy. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
* **"-I, --attack-interval"**: (Default: `0ms`) Set custom interval after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on fast and reliable networks.
* **"-T, --timeout"**: (Default: `2000ms`) Set custom timeout value after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on fast and reliable networks.
* **"-r, --custom-routes"**: (Default: `<CAMERADAR_GOPATH>/dictionaries/routes`) Set custom dictionary path for routes
* **"-c, --custom-credentials"**: (Default: `<CAMERADAR_GOPATH>/dictionaries/credentials.json`) Set custom dictionary path for credentials
* **"-o, --nmap-output"**: (Default: `/tmp/cameradar_scan.xml`) Set custom nmap output path
* **"-l, --log"**: Enable debug logs (nmap requests, curl describe requests, etc.)
* **"-h"** : Display the usage information
* **"-d, --debug"**: Enable debug logs
* **"-v, --verbose"**: Enable verbose curl logs (not recommended for most use)
* **"-h"**: Display the usage information
## Format input file
The file can contain IPs, hostnames, IP ranges and subnetwork, separated by newlines. Example:
```go
```text
0.0.0.0
localhost
192.17.0.0/16
@@ -223,17 +177,23 @@ These variables are optional, allowing to replace the default dictionaries with
Default values: `<CAMERADAR_GOPATH>/dictionaries/routes` and `<CAMERADAR_GOPATH>/dictionaries/credentials.json`
### `CAMERADAR_SPEED`
### `CAMERADAR_SCAN_SPEED`
This optional variable allows you to set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a very performant and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
This optional variable allows you to set custom nmap discovery presets to improve speed or accuracy. It's recommended to lower it if you are attempting to scan an unstable and slow network, or to increase it if on a fast and reliable network. See [this for more info on the nmap timing templates](https://nmap.org/book/man-performance.html).
Default value: `4`
### `CAMERADAR_ATTACK_INTERVAL`
This optional variable allows you to set `custom interval` to wait between each attack in order to stay stealthy. It's recommended to increase it when attempting to scan a network that might be protected against bruteforce attacks. By default, there is no interval, in order to make attacks as fast as possible
Default value: `0ms`
### `CAMERADAR_TIMEOUT`
This optional variable allows you to set custom timeout value in miliseconds after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on very performant and reliable networks.
This optional variable allows you to set custom timeout value after which an attack attempt without an answer should give up. It's recommended to increase it when attempting to scan unstable and slow networks or to decrease it on fast and reliable networks.
Default value: `2000`
Default value: `2000ms`
### `CAMERADAR_LOGGING`
@@ -255,48 +215,46 @@ Your image will be called `cameradar` and NOT `ullaakut/cameradar`.
#### Go build
To build the project without docker:
1. `go get github.com/Ullaakut/cameradar`
2. `cd $GOPATH/src/github.com/Ullaakut/cameradar`
3. `cd cmd/cameradar`
4. `go install`
1. Install dep
* OSX: `brew install dep` and `brew upgrade dep`
* Others: Download the release package for your OS [here](https://github.com/golang/dep/releases)
2. `dep ensure`
3. `go build` to build the library
4. `cd cameradar && go build` to build the binary
The cameradar binary is now in the root of the directory.
See [the contribution document](/CONTRIBUTING.md) to get started.
The cameradar binary is now in `$GOPATH/bin/cameradar`.
## Frequently Asked Questions
> Cameradar does not detect any camera!
That means that either your cameras are not streaming in RTSP or that they are not on the target you are scanning. In most cases, CCTV cameras will be on a private subnetwork, isolated from the internet. Use the `-t` option to specify your target.
That means that either your cameras are not streaming in RTSP or that they are not on the target you are scanning. In most cases, CCTV cameras will be on a private subnetwork, isolated from the internet. Use the `-t` option to specify your target. If you are sure you did everything right but it still does not work, please open an issue with details on the device you are trying to access 🙏
> Cameradar detects my cameras, but does not manage to access them at all!
Maybe your cameras have been configured and the credentials / URL have been changed. Cameradar only guesses using default constructor values if a custom dictionary is not provided. You can use your own dictionaries in which you just have to add your credentials and RTSP routes. To do that, see how the [configuration](#configuration) works. Also, maybe your camera's credentials are not yet known, in which case if you find them it would be very nice to add them to the Cameradar dictionaries to help other people in the future.
Maybe your cameras have been configured, and the credentials / URL have been changed. Cameradar only guesses using default constructor values if a custom dictionary is not provided. You can use your own dictionaries in which you just have to add your credentials and RTSP routes. To do that, see how the [configuration](#configuration) works. Also, maybe your camera's credentials are not yet known, in which case if you find them it would be very nice to add them to the Cameradar dictionaries to help other people in the future.
> What happened to the C++ version?
You can still find it under the 1.1.4 tag on this repo, however it was less performant and stable than the current version written in Golang.
You can still find it under the 1.1.4 tag on this repo, however it was slower and less stable than the current version written in Golang. It is not recommended using it.
> How to use the Cameradar library for my own project?
See the example in `/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cmrdr` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/Ullaakut/cameradar).
See the example in `/cmd/cameradar`. You just need to run `go get github.com/Ullaakut/cameradar` and to use the `cameradar` package in your code. You can find the documentation on [godoc](https://godoc.org/github.com/ullaakut/cameradar).
> I want to scan my own localhost for some reason and it does not work! What's going on?
> I want to scan my own localhost for some reason, and it does not work! What's going on?
Use the `--net=host` flag when launching the cameradar image, or use the binary by running `go run cameradar/cameradar.go` or [installing it](#installing-the-binary)
Use the `--net=host` flag when launching the cameradar image, or use the binary by running `go run cameradar/cameradar.go` or [installing it](#go-build).
> I don't see a colored output :(
> I don't see a colored output:(
You forgot the `-t` flag before `ullaakut/cameradar` in your command-line. This tells docker to allocate a pseudo-tty for cameradar, which makes it able to use colors.
> I don't have a camera but I'd like to try Cameradar!
> I don't have a camera, but I'd like to try Cameradar!
Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -e RTSP_PORT=8554 ullaakut/rtspatt` and then run cameradar and it should guess that the username is admin and the password is 12345. You can try this with any default constructor credentials (they can be found [here](dictionaries/credentials.json))
Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -e RTSP_PORT=8554 ullaakut/rtspatt` and then run cameradar, and it should guess that the username is admin and that the password is 12345. You can try this with any default constructor credentials (they can be found [here](dictionaries/credentials.json)).
> What authentication types does Cameradar support?
Cameradar supports both basic and digest authentication.
## Examples
@@ -306,11 +264,15 @@ Simply run `docker run -p 8554:8554 -e RTSP_USERNAME=admin -e RTSP_PASSWORD=1234
> Running cameradar with an input file, logs enabled on port 8554
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t /tmp/test.txt -p 8554 -l`
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t /tmp/test.txt -p 8554`
> Running cameradar on a subnetwork with custom dictionaries, on ports 554, 5554 and 8554
`docker run -v /tmp:/tmp --net=host -t ullaakut/cameradar -t 192.168.0.0/24 --custom-credentials="/tmp/dictionaries/credentials.json" --custom-routes="/tmp/dictionaries/routes" -p 554,5554,8554`
## License
Copyright 2017 Ullaakut
Copyright 2019 Ullaakut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+293 -178
View File
@@ -1,15 +1,13 @@
package cmrdr
package cameradar
import (
"fmt"
"time"
curl "github.com/andelf/go-curl"
"github.com/pkg/errors"
v "gopkg.in/go-playground/validator.v9"
"github.com/Ullaakut/go-curl"
)
// HTTP responses
// HTTP responses.
const (
httpOK = 200
httpUnauthorized = 401
@@ -17,18 +15,218 @@ const (
httpNotFound = 404
)
// CURL RTSP request types
// CURL RTSP request types.
const (
rtspDescribe = 2
rtspSetup = 4
)
// HACK: See https://stackoverflow.com/questions/3572397/lib-curl-in-c-disable-printing
func doNotWrite([]uint8, interface{}) bool {
return true
// Authentication types.
const (
authNone = 0
authBasic = 1
authDigest = 2
)
// Route that should never be a constructor default.
const dummyRoute = "/0x8b6c42"
// Attack attacks the given targets and returns the accessed streams.
func (s *Scanner) Attack(targets []Stream) ([]Stream, error) {
if len(targets) == 0 {
return nil, fmt.Errorf("no stream found")
}
// Most cameras will be accessed successfully with these two attacks.
s.term.StartStepf("Attacking routes of %d streams", len(targets))
streams := s.AttackRoute(targets)
s.term.StartStepf("Attempting to detect authentication methods of %d streams", len(targets))
streams = s.DetectAuthMethods(streams)
s.term.StartStepf("Attacking credentials of %d streams", len(targets))
streams = s.AttackCredentials(streams)
s.term.StartStep("Validating that streams are accessible")
streams = s.ValidateStreams(streams)
// But some cameras run GST RTSP Server which prioritizes 401 over 404 contrary to most cameras.
// For these cameras, running another route attack will solve the problem.
for _, stream := range streams {
if !stream.RouteFound || !stream.CredentialsFound || !stream.Available {
s.term.StartStepf("Second round of attacks")
streams = s.AttackRoute(streams)
s.term.StartStep("Validating that streams are accessible")
streams = s.ValidateStreams(streams)
break
}
}
s.term.EndStep()
return streams, nil
}
func routeAttack(c Curler, stream Stream, route string, timeout time.Duration, enableLogs bool) bool {
// ValidateStreams tries to setup the stream to validate whether or not it is available.
func (s *Scanner) ValidateStreams(targets []Stream) []Stream {
for i := range targets {
targets[i].Available = s.validateStream(targets[i])
time.Sleep(s.attackInterval)
}
return targets
}
// AttackCredentials attempts to guess the provided targets' credentials using the given
// dictionary or the default dictionary if none was provided by the user.
func (s *Scanner) AttackCredentials(targets []Stream) []Stream {
resChan := make(chan Stream)
defer close(resChan)
for i := range targets {
go s.attackCameraCredentials(targets[i], resChan)
}
for range targets {
attackResult := <-resChan
if attackResult.CredentialsFound {
targets = replace(targets, attackResult)
}
}
return targets
}
// AttackRoute attempts to guess the provided targets' streaming routes using the given
// dictionary or the default dictionary if none was provided by the user.
func (s *Scanner) AttackRoute(targets []Stream) []Stream {
resChan := make(chan Stream)
defer close(resChan)
for i := range targets {
go s.attackCameraRoute(targets[i], resChan)
}
for range targets {
attackResult := <-resChan
if attackResult.RouteFound {
targets = replace(targets, attackResult)
}
}
return targets
}
// DetectAuthMethods attempts to guess the provided targets' authentication types, between
// digest, basic auth or none at all.
func (s *Scanner) DetectAuthMethods(targets []Stream) []Stream {
for i := range targets {
targets[i].AuthenticationType = s.detectAuthMethod(targets[i])
time.Sleep(s.attackInterval)
var authMethod string
switch targets[i].AuthenticationType {
case authNone:
authMethod = "no"
case authBasic:
authMethod = "basic"
case authDigest:
authMethod = "digest"
}
s.term.Debugf("Stream %s uses %s authentication method\n", GetCameraRTSPURL(targets[i]), authMethod)
}
return targets
}
func (s *Scanner) attackCameraCredentials(target Stream, resChan chan<- Stream) {
for _, username := range s.credentials.Usernames {
for _, password := range s.credentials.Passwords {
ok := s.credAttack(target, username, password)
if ok {
target.CredentialsFound = true
target.Username = username
target.Password = password
resChan <- target
return
}
time.Sleep(s.attackInterval)
}
}
target.CredentialsFound = false
resChan <- target
}
func (s *Scanner) attackCameraRoute(target Stream, resChan chan<- Stream) {
// If the stream responds positively to the dummy route, it means
// it doesn't require (or respect the RFC) a route and the attack
// can be skipped.
ok := s.routeAttack(target, dummyRoute)
if ok {
target.RouteFound = true
target.Routes = append(target.Routes, "/")
resChan <- target
return
}
// Otherwise, bruteforce the routes.
for _, route := range s.routes {
ok := s.routeAttack(target, route)
if ok {
target.RouteFound = true
target.Routes = append(target.Routes, route)
}
time.Sleep(s.attackInterval)
}
resChan <- target
}
func (s *Scanner) detectAuthMethod(stream Stream) int {
c := s.curl.Duphandle()
attackURL := fmt.Sprintf(
"rtsp://%s:%d/%s",
stream.Address,
stream.Port,
stream.Route(),
)
s.setCurlOptions(c)
// Send a request to the URL of the stream we want to attack.
_ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
// Perform the request.
err := c.Perform()
if err != nil {
s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return -1
}
authType, err := c.Getinfo(curl.INFO_HTTPAUTH_AVAIL)
if err != nil {
s.term.Errorf("Getinfo failed: %v", err)
return -1
}
if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", authType)
}
return authType.(int)
}
func (s *Scanner) routeAttack(stream Stream, route string) bool {
c := s.curl.Duphandle()
attackURL := fmt.Sprintf(
"rtsp://%s:%s@%s:%d/%s",
stream.Username,
@@ -38,246 +236,163 @@ func routeAttack(c Curler, stream Stream, route string, timeout time.Duration, e
route,
)
if enableLogs {
// Debug logs when logs are enabled
c.Setopt(curl.OPT_VERBOSE, 1)
} else {
// Do not write sdp in stdout
c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
}
s.setCurlOptions(c)
// Do not use signals (would break multithreading)
c.Setopt(curl.OPT_NOSIGNAL, 1)
// Do not send a body in the describe request
c.Setopt(curl.OPT_NOBODY, 1)
// Send a request to the URL of the stream we want to attack
c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL
c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_DESCRIBE
c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
// Set custom timeout
c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
// Set proper authentication type.
_ = c.Setopt(curl.OPT_HTTPAUTH, stream.AuthenticationType)
_ = c.Setopt(curl.OPT_USERPWD, fmt.Sprint(stream.Username, ":", stream.Password))
// Perform the request
// Send a request to the URL of the stream we want to attack.
_ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
// Perform the request.
err := c.Perform()
if err != nil {
s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false
}
// Get return code for the request
// Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil {
s.term.Errorf("Getinfo failed: %v", err)
return false
}
// If it's a 401 or 403, it means that the credentials are wrong but the route might be okay
// If it's a 200, the stream is accessed successfully
if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc)
}
// If it's a 401 or 403, it means that the credentials are wrong but the route might be okay.
// If it's a 200, the stream is accessed successfully.
if rc == httpOK || rc == httpUnauthorized || rc == httpForbidden {
return true
}
return false
}
func credAttack(c Curler, stream Stream, username string, password string, timeout time.Duration, enableLogs bool) bool {
func (s *Scanner) credAttack(stream Stream, username string, password string) bool {
c := s.curl.Duphandle()
attackURL := fmt.Sprintf(
"rtsp://%s:%s@%s:%d/%s",
username,
password,
stream.Address,
stream.Port,
stream.Route,
stream.Route(),
)
if enableLogs {
// Debug logs when logs are enabled
c.Setopt(curl.OPT_VERBOSE, 1)
} else {
// Do not write sdp in stdout
c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
}
s.setCurlOptions(c)
// Do not use signals (would break multithreading)
c.Setopt(curl.OPT_NOSIGNAL, 1)
// Do not send a body in the describe request
c.Setopt(curl.OPT_NOBODY, 1)
// Send a request to the URL of the stream we want to attack
c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL
c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_DESCRIBE
c.Setopt(curl.OPT_RTSP_REQUEST, 2)
// Set custom timeout
c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
// Set proper authentication type.
_ = c.Setopt(curl.OPT_HTTPAUTH, stream.AuthenticationType)
_ = c.Setopt(curl.OPT_USERPWD, fmt.Sprint(username, ":", password))
// Perform the request
// Send a request to the URL of the stream we want to attack.
_ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspDescribe)
// Perform the request.
err := c.Perform()
if err != nil {
s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false
}
// Get return code for the request
// Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil {
s.term.Errorf("Getinfo failed: %v", err)
return false
}
// If it's a 404, it means that the route is incorrect but the credentials might be okay
// If it's a 200, the stream is accessed successfully
if s.debug {
s.term.Debugln("DESCRIBE", attackURL, "RTSP/1.0 >", rc)
}
// If it's a 404, it means that the route is incorrect but the credentials might be okay.
// If it's a 200, the stream is accessed successfully.
if rc == httpOK || rc == httpNotFound {
return true
}
return false
}
func validateStream(c Curler, stream Stream, timeout time.Duration, enableLogs bool) bool {
func (s *Scanner) validateStream(stream Stream) bool {
c := s.curl.Duphandle()
attackURL := fmt.Sprintf(
"rtsp://%s:%s@%s:%d/%s",
stream.Username,
stream.Password,
stream.Address,
stream.Port,
stream.Route,
stream.Route(),
)
if enableLogs {
// Debug logs when logs are enabled
c.Setopt(curl.OPT_VERBOSE, 1)
} else {
// Do not write sdp in stdout
c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
}
s.setCurlOptions(c)
// Do not use signals (would break multithreading)
c.Setopt(curl.OPT_NOSIGNAL, 1)
// Do not send a body in the describe request
c.Setopt(curl.OPT_NOBODY, 1)
// Send a request to the URL of the stream we want to attack
c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL
c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
// 2 is CURL_RTSPREQ_SETUP
c.Setopt(curl.OPT_RTSP_REQUEST, rtspSetup)
// Set custom timeout
c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
// Set proper authentication type.
_ = c.Setopt(curl.OPT_HTTPAUTH, stream.AuthenticationType)
_ = c.Setopt(curl.OPT_USERPWD, fmt.Sprint(stream.Username, ":", stream.Password))
c.Setopt(curl.OPT_RTSP_TRANSPORT, "RTP/AVP;unicast;client_port=33332-33333")
// Send a request to the URL of the stream we want to attack.
_ = c.Setopt(curl.OPT_URL, attackURL)
// Set the RTSP STREAM URI as the stream URL.
_ = c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
_ = c.Setopt(curl.OPT_RTSP_REQUEST, rtspSetup)
// Perform the request
_ = c.Setopt(curl.OPT_RTSP_TRANSPORT, "RTP/AVP;unicast;client_port=33332-33333")
// Perform the request.
err := c.Perform()
if err != nil {
s.term.Errorf("Perform failed for %q (auth %d): %v", attackURL, stream.AuthenticationType, err)
return false
}
// Get return code for the request
// Get return code for the request.
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
if err != nil {
s.term.Errorf("Getinfo failed: %v", err)
return false
}
// If it's a 200, the stream is accessed successfully
if s.debug {
s.term.Debugln("SETUP", attackURL, "RTSP/1.0 >", rc)
}
// If it's a 200, the stream is accessed successfully.
if rc == httpOK {
return true
}
return false
}
// ValidateStreams tries to setup the stream to validate whether or not it is available
func ValidateStreams(c Curler, targets []Stream, timeout time.Duration, log bool) ([]Stream, error) {
for idx, target := range targets {
targets[idx].Available = validateStream(c, target, timeout, log)
}
func (s *Scanner) setCurlOptions(c Curler) {
// Do not write sdp in stdout
_ = c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
// Do not use signals (would break multithreading).
_ = c.Setopt(curl.OPT_NOSIGNAL, 1)
// Do not send a body in the describe request.
_ = c.Setopt(curl.OPT_NOBODY, 1)
// Set custom timeout.
_ = c.Setopt(curl.OPT_TIMEOUT_MS, int(s.timeout/time.Millisecond))
return targets, nil
// Enable verbose logs if verbose mode is on.
if s.verbose {
_ = c.Setopt(curl.OPT_VERBOSE, 1)
} else {
_ = c.Setopt(curl.OPT_VERBOSE, 0)
}
}
func attackCameraCredentials(c Curler, target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) {
for _, username := range credentials.Usernames {
for _, password := range credentials.Passwords {
ok := credAttack(c.Duphandle(), target, username, password, timeout, log)
if ok {
target.CredentialsFound = true
target.Username = username
target.Password = password
resultsChan <- target
return
}
}
}
target.CredentialsFound = false
resultsChan <- target
}
func attackCameraRoute(c Curler, target Stream, routes Routes, resultsChan chan<- Stream, timeout time.Duration, log bool) {
for _, route := range routes {
ok := routeAttack(c.Duphandle(), target, route, timeout, log)
if ok {
target.RouteFound = true
target.Route = route
resultsChan <- target
return
}
}
target.RouteFound = false
resultsChan <- target
}
// AttackCredentials attempts to guess the provided targets' credentials using the given
// dictionary or the default dictionary if none was provided by the user.
func AttackCredentials(c Curler, targets []Stream, credentials Credentials, timeout time.Duration, log bool) ([]Stream, error) {
attacks := make(chan Stream)
defer close(attacks)
validate := v.New()
for _, target := range targets {
err := validate.Struct(target)
if err != nil {
return targets, errors.Wrap(err, "invalid targets")
}
go attackCameraCredentials(c, target, credentials, attacks, timeout, log)
}
attackResults := []Stream{}
for range targets {
attackResults = append(attackResults, <-attacks)
}
for _, result := range attackResults {
if result.CredentialsFound {
targets = replace(targets, result)
}
}
return targets, nil
}
// AttackRoute attempts to guess the provided targets' streaming routes using the given
// dictionary or the default dictionary if none was provided by the user.
func AttackRoute(c Curler, targets []Stream, routes Routes, timeout time.Duration, log bool) ([]Stream, error) {
attacks := make(chan Stream)
defer close(attacks)
validate := v.New()
for _, target := range targets {
err := validate.Struct(target)
if err != nil {
return targets, errors.Wrap(err, "invalid targets")
}
go attackCameraRoute(c, target, routes, attacks, timeout, log)
}
attackResults := []Stream{}
for range targets {
attackResults = append(attackResults, <-attacks)
}
for _, result := range attackResults {
if result.RouteFound {
targets = replace(targets, result)
}
}
return targets, nil
// HACK: See https://stackoverflow.com/questions/3572397/lib-curl-in-c-disable-printing
func doNotWrite([]uint8, interface{}) bool {
return true
}
+487 -222
View File
@@ -1,13 +1,13 @@
package cmrdr
package cameradar
import (
"errors"
"fmt"
"os"
"io/ioutil"
"testing"
"time"
curl "github.com/andelf/go-curl"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/go-curl"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
@@ -35,35 +35,127 @@ func (m *CurlerMock) Duphandle() Curler {
return m
}
func TestAttack(t *testing.T) {
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
}
fakeTargets = []Stream{stream1, stream2}
fakeRoutes = Routes{"live.sdp", "media.amp"}
fakeCredentials = Credentials{
Usernames: []string{"admin", "root"},
Passwords: []string{"12345", "root"},
}
)
tests := []struct {
description string
targets []Stream
performErr error
expectedStreams []Stream
expectedErr error
}{
{
description: "inverted RTSP RFC",
targets: fakeTargets,
performErr: errors.New("dummy error"),
expectedStreams: fakeTargets,
},
{
description: "attack works",
targets: fakeTargets,
expectedStreams: fakeTargets,
},
{
description: "no targets",
targets: nil,
expectedStreams: nil,
expectedErr: errors.New("no stream found"),
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
if len(test.targets) != 0 {
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(200, nil)
}
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: time.Millisecond,
verbose: true,
debug: true,
credentials: fakeCredentials,
routes: fakeRoutes,
}
results, err := scanner.Attack(test.targets)
assert.Equal(t, test.expectedErr, err)
assert.Len(t, results, len(test.expectedStreams))
curlerMock.AssertExpectations(t)
})
}
}
func TestAttackCredentials(t *testing.T) {
validStream1 := Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
}
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
validStream2 := Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
invalidStream := Stream{
Device: "InvalidDevice",
}
fakeTargets = []Stream{stream1, stream2}
fakeCredentials = Credentials{
Usernames: []string{"admin", "root"},
Passwords: []string{"12345", "root"},
}
)
fakeTargets := []Stream{validStream1, validStream2}
invalidTargets := []Stream{invalidStream}
fakeCredentials := Credentials{
Usernames: []string{"admin", "root"},
Passwords: []string{"12345", "root"},
}
tests := []struct {
description string
testCases := []struct {
targets []Stream
credentials Credentials
timeout time.Duration
log bool
verbose bool
status int
@@ -72,10 +164,10 @@ func TestAttackCredentials(t *testing.T) {
invalidTargets bool
expectedStreams []Stream
expectedErrMsg string
}{
// Credentials found
{
description: "Credentials found",
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
@@ -84,8 +176,9 @@ func TestAttackCredentials(t *testing.T) {
expectedStreams: fakeTargets,
},
// Camera accessed
{
description: "Camera accessed",
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
@@ -94,19 +187,9 @@ func TestAttackCredentials(t *testing.T) {
expectedStreams: fakeTargets,
},
// Invalid targets
{
targets: invalidTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
description: "curl perform fails",
invalidTargets: true,
expectedErrMsg: "invalid targets",
expectedStreams: invalidTargets,
},
// curl perform fails
{
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
@@ -115,8 +198,9 @@ func TestAttackCredentials(t *testing.T) {
expectedStreams: fakeTargets,
},
// curl getinfo fails
{
description: "curl getinfo fails",
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
@@ -125,94 +209,89 @@ func TestAttackCredentials(t *testing.T) {
expectedStreams: fakeTargets,
},
// Logging disabled
{
description: "Verbose mode disabled",
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
log: false,
verbose: false,
status: 403,
expectedStreams: fakeTargets,
},
// Logging enabled
{
description: "Verbose mode enabled",
targets: fakeTargets,
credentials: fakeCredentials,
timeout: 1 * time.Millisecond,
log: true,
verbose: true,
status: 403,
expectedStreams: fakeTargets,
},
}
for i, test := range testCases {
curlerMock := &CurlerMock{}
if !test.invalidTargets {
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
results, err := AttackCredentials(curlerMock, test.targets, test.credentials, test.timeout, test.log)
if len(test.expectedErrMsg) > 0 {
if err == nil {
fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1)
}
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in AttackCredentials test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, stream := range test.expectedStreams {
foundStream := false
for _, result := range results {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
if !test.invalidTargets {
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
assert.Equal(t, true, foundStream, "wrong streams parsed")
}
}
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
curlerMock.AssertExpectations(t)
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
debug: test.verbose,
credentials: test.credentials,
}
results := scanner.AttackCredentials(test.targets)
assert.Len(t, results, len(test.expectedStreams))
curlerMock.AssertExpectations(t)
})
}
}
func TestAttackRoute(t *testing.T) {
validStream1 := Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
}
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
validStream2 := Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
invalidStream := Stream{
Device: "InvalidDevice",
}
fakeTargets = []Stream{stream1, stream2}
fakeRoutes = Routes{"live.sdp", "media.amp"}
)
fakeTargets := []Stream{validStream1, validStream2}
fakeRoutes := Routes{"live.sdp", "media.amp"}
invalidTargets := []Stream{invalidStream}
tests := []struct {
description string
testCases := []struct {
targets []Stream
routes Routes
timeout time.Duration
log bool
verbose bool
status int
@@ -221,10 +300,11 @@ func TestAttackRoute(t *testing.T) {
invalidTargets bool
expectedStreams []Stream
expectedErrMsg string
expectedErr error
}{
// Route found
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
@@ -233,8 +313,9 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
// Route found
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
@@ -243,8 +324,9 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
// Camera accessed
{
description: "Camera accessed",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
@@ -253,18 +335,9 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
// Invalid targets
{
targets: invalidTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
invalidTargets: true,
description: "curl perform fails",
expectedErrMsg: "invalid targets",
expectedStreams: invalidTargets,
},
// curl perform fails
{
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
@@ -273,8 +346,9 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
// curl getinfo fails
{
description: "curl getinfo fails",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
@@ -283,99 +357,178 @@ func TestAttackRoute(t *testing.T) {
expectedStreams: fakeTargets,
},
// Logs disabled
{
description: "verbose mode disabled",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
log: false,
verbose: false,
expectedStreams: fakeTargets,
},
// Logs enabled
{
description: "verbose mode enabled",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
log: true,
verbose: true,
expectedStreams: fakeTargets,
},
}
for i, test := range testCases {
curlerMock := &CurlerMock{}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
if !test.invalidTargets {
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
}
results, err := AttackRoute(curlerMock, test.targets, test.routes, test.timeout, test.log)
if len(test.expectedErrMsg) > 0 {
if err == nil {
fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1)
}
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, stream := range test.expectedStreams {
foundStream := false
for _, result := range results {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
if !test.invalidTargets {
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
assert.Equal(t, true, foundStream, "wrong streams parsed")
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
debug: test.verbose,
routes: test.routes,
}
results := scanner.AttackRoute(test.targets)
assert.Len(t, results, len(test.expectedStreams))
curlerMock.AssertExpectations(t)
})
}
}
func TestAttackRoute_NoDummyRoute(t *testing.T) {
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
curlerMock.AssertExpectations(t)
fakeTargets = []Stream{stream1, stream2}
fakeRoutes = Routes{"live.sdp", "media.amp"}
)
tests := []struct {
description string
targets []Stream
routes Routes
timeout time.Duration
verbose bool
status int
expectedStreams []Stream
expectedErr error
}{
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 403,
expectedStreams: fakeTargets,
},
{
description: "Route found",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 401,
expectedStreams: fakeTargets,
},
{
description: "Camera accessed",
targets: fakeTargets,
routes: fakeRoutes,
timeout: 1 * time.Millisecond,
status: 200,
expectedStreams: fakeTargets,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(nil)
// 404 on first call to the dummy route.
curlerMock.On("Getinfo", mock.Anything).Return(404, nil).Once()
curlerMock.On("Getinfo", mock.Anything).Return(test.status, nil)
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
routes: test.routes,
}
results := scanner.AttackRoute(test.targets)
assert.Len(t, results, len(test.expectedStreams))
curlerMock.AssertExpectations(t)
})
}
}
func TestValidateStreams(t *testing.T) {
validStream1 := Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
validStream2 := Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
unavailableStream := Stream{
Device: "fakeDevice",
Available: false,
}
fakeTargets = []Stream{stream1, stream2}
)
fakeTargets := []Stream{validStream1, validStream2}
unavailableTargets := []Stream{unavailableStream}
testCases := []struct {
desc string
tests := []struct {
description string
targets []Stream
timeout time.Duration
log bool
verbose bool
status int
@@ -383,11 +536,9 @@ func TestValidateStreams(t *testing.T) {
getInfoErr error
expectedStreams []Stream
expectedErrMsg string
}{
// Route found
{
desc: "route found",
description: "route found",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
@@ -396,9 +547,8 @@ func TestValidateStreams(t *testing.T) {
expectedStreams: fakeTargets,
},
// Route found
{
desc: "route found",
description: "route found",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
@@ -407,9 +557,8 @@ func TestValidateStreams(t *testing.T) {
expectedStreams: fakeTargets,
},
// Camera accessed
{
desc: "camera accessed",
description: "camera accessed",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
@@ -418,20 +567,18 @@ func TestValidateStreams(t *testing.T) {
expectedStreams: fakeTargets,
},
// Unavailable stream
{
desc: "unavailable stream",
description: "unavailable stream",
targets: unavailableTargets,
targets: fakeTargets,
timeout: 1 * time.Millisecond,
status: 400,
expectedStreams: unavailableTargets,
expectedStreams: fakeTargets,
},
// curl perform fails
{
desc: "curl perform fails",
description: "curl perform fails",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
@@ -440,9 +587,8 @@ func TestValidateStreams(t *testing.T) {
expectedStreams: fakeTargets,
},
// curl getinfo fails
{
desc: "curl getinfo fails",
description: "curl getinfo fails",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
@@ -451,65 +597,184 @@ func TestValidateStreams(t *testing.T) {
expectedStreams: fakeTargets,
},
// Logs disabled
{
desc: "logs disabled",
description: "verbose disabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
log: false,
verbose: false,
expectedStreams: fakeTargets,
},
// Logs enabled
{
desc: "logs enabled",
description: "verbose enabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
log: true,
verbose: true,
expectedStreams: fakeTargets,
},
}
for i, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(tC.performErr)
if tC.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(tC.status, tC.getInfoErr)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
results, err := ValidateStreams(curlerMock, tC.targets, tC.timeout, tC.log)
if len(tC.expectedErrMsg) > 0 {
if err == nil {
fmt.Printf("unexpected success in ValidateStream test, iteration %d. expected error: %s\n", i, tC.expectedErrMsg)
os.Exit(1)
}
assert.Contains(t, err.Error(), tC.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in ValidateStream test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, stream := range tC.expectedStreams {
foundStream := false
for _, result := range results {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
}
assert.Equal(t, true, foundStream, "wrong streams parsed")
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
debug: test.verbose,
}
assert.Equal(t, len(tC.expectedStreams), len(results), "wrong streams parsed")
results := scanner.ValidateStreams(test.targets)
assert.Equal(t, len(test.expectedStreams), len(results))
for _, expectedStream := range test.expectedStreams {
assert.Contains(t, results, expectedStream)
}
curlerMock.AssertExpectations(t)
})
}
}
func TestDetectAuthenticationType(t *testing.T) {
var (
stream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Available: true,
}
stream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Available: true,
}
fakeTargets = []Stream{stream1, stream2}
)
tests := []struct {
description string
targets []Stream
timeout time.Duration
verbose bool
status int
performErr error
getInfoErr error
expectedStreams []Stream
}{
{
description: "no auth enabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
status: 0,
expectedStreams: fakeTargets,
},
{
description: "basic auth enabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
status: 1,
expectedStreams: fakeTargets,
},
{
description: "digest auth enabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
status: 2,
expectedStreams: fakeTargets,
},
{
description: "curl getinfo fails",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
getInfoErr: errors.New("dummy error"),
expectedStreams: fakeTargets,
},
{
description: "curl perform fails",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
performErr: errors.New("dummy error"),
expectedStreams: fakeTargets,
},
{
description: "verbose disabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
verbose: false,
expectedStreams: fakeTargets,
},
{
description: "verbose enabled",
targets: fakeTargets,
timeout: 1 * time.Millisecond,
verbose: true,
expectedStreams: fakeTargets,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
curlerMock := &CurlerMock{}
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
curlerMock.On("Perform").Return(test.performErr)
if test.performErr == nil {
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
curl: curlerMock,
timeout: test.timeout,
verbose: test.verbose,
}
results := scanner.DetectAuthMethods(test.targets)
assert.Equal(t, len(test.expectedStreams), len(results))
for _, expectedStream := range test.expectedStreams {
assert.Contains(t, results, expectedStream)
}
curlerMock.AssertExpectations(t)
})
+2 -2
View File
@@ -1,4 +1,4 @@
// Package cmrdr provides methods to be able to discover and
// Package cameradar provides methods to be able to discover and
// attack RTSP streams easily. RTSP streams are used by most
// IP Cameras, often for surveillance.
//
@@ -11,4 +11,4 @@
// access cameras, or running their own network scan, this
// library allows to use simple and performant methods to
// attack streams.
package cmrdr
package cameradar
-245
View File
@@ -1,245 +0,0 @@
package main
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/ullaakut/cameradar"
curl "github.com/andelf/go-curl"
"github.com/fatih/color"
"github.com/gernest/wow"
"github.com/gernest/wow/spin"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
type options struct {
Targets []string
Ports []string
Routes string
Credentials string
Speed int
Timeout int
EnableLogs bool
}
func parseArguments() error {
viper.SetEnvPrefix("cameradar")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
pflag.StringSliceP("targets", "t", []string{}, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
pflag.StringSliceP("ports", "p", []string{"554", "5554", "8554"}, "The ports on which to search for RTSP streams")
pflag.StringP("custom-routes", "r", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
pflag.StringP("custom-credentials", "c", "<GOPATH>/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json", "The path on which to load a custom credentials JSON dictionary")
pflag.IntP("speed", "s", 4, "The nmap speed preset to use for discovery")
pflag.IntP("timeout", "T", 2000, "The timeout in miliseconds to use for attack attempts")
pflag.BoolP("log", "l", false, "Enable the logs for nmap's output to stdout")
pflag.BoolP("help", "h", false, "displays this help message")
viper.AutomaticEnv()
pflag.Parse()
err := viper.BindPFlags(pflag.CommandLine)
if err != nil {
return err
}
if viper.GetBool("help") {
pflag.Usage()
fmt.Println("\nExamples of usage:")
fmt.Println("\tScanning your home network for RTSP streams:\tcameradar -t 192.168.0.0/24")
fmt.Println("\tScanning a remote camera on a specific port:\tcameradar -t 172.178.10.14 -p 18554 -s 2")
fmt.Println("\tScanning an unstable remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 --timeout 10000 -l")
os.Exit(0)
}
if viper.GetStringSlice("targets") == nil {
return errors.New("targets (-t, --targets) argument required\n examples:\n - 172.16.100.0/24\n - localhost\n - 8.8.8.8")
}
return nil
}
func main() {
var options options
err := parseArguments()
if err != nil {
printErr(err)
}
options.Credentials = viper.GetString("custom-credentials")
options.EnableLogs = viper.GetBool("log") || viper.GetBool("logging")
options.Ports = viper.GetStringSlice("ports")
options.Routes = viper.GetString("custom-routes")
options.Speed = viper.GetInt("speed")
options.Timeout = viper.GetInt("timeout")
options.Targets = viper.GetStringSlice("targets")
w := startSpinner(options.EnableLogs)
if len(options.Targets) == 1 {
options.Targets, err = cmrdr.ParseTargetsFile(options.Targets[0])
if err != nil {
printErr(err)
}
}
err = curl.GlobalInit(curl.GLOBAL_ALL)
handle := curl.EasyInit()
if err != nil || handle == nil {
printErr(errors.New("libcurl initialization failed"))
}
c := &cmrdr.Curl{CURL: handle}
defer curl.GlobalCleanup()
updateSpinner(w, "Loading dictionaries...", options.EnableLogs)
gopath := os.Getenv("GOPATH")
options.Credentials = strings.Replace(options.Credentials, "<GOPATH>", gopath, 1)
options.Routes = strings.Replace(options.Routes, "<GOPATH>", gopath, 1)
credentials, err := cmrdr.LoadCredentials(options.Credentials)
if err != nil {
color.Red("Invalid credentials dictionary: %s", err.Error())
return
}
routes, err := cmrdr.LoadRoutes(options.Routes)
if err != nil {
color.Red("Invalid routes dictionary: %s", err.Error())
return
}
updateSpinner(w, "Scanning the network...", options.EnableLogs)
streams, err := cmrdr.Discover(options.Targets, options.Ports, options.Speed)
if err != nil && len(streams) > 0 {
printErr(err)
}
// Most cameras will be accessed successfully with these two attacks
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", options.EnableLogs)
streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
}
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs)
streams, err = cmrdr.AttackCredentials(c, streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
}
// But some cameras run GST RTSP Server which prioritizes 401 over 404 contrary to most cameras.
// For these cameras, running another route attack will solve the problem.
for _, stream := range streams {
if !stream.RouteFound || !stream.CredentialsFound {
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs)
streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
}
break
}
}
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Validating their availability...", options.EnableLogs)
streams, err = cmrdr.ValidateStreams(c, streams, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
if err != nil && len(streams) > 0 {
printErr(err)
}
clearOutput(w, options.EnableLogs)
prettyPrint(streams)
}
func prettyPrint(streams []cmrdr.Stream) {
yellow := color.New(color.FgYellow, color.Bold, color.Underline).SprintFunc()
blue := color.New(color.FgBlue, color.Underline).SprintFunc()
green := color.New(color.FgGreen, color.Bold).SprintFunc()
red := color.New(color.FgRed, color.Bold).SprintFunc()
white := color.New(color.Italic).SprintFunc()
success := 0
if len(streams) > 0 {
for _, stream := range streams {
if stream.CredentialsFound && stream.RouteFound && stream.Available {
fmt.Printf("%s\tDevice RTSP URL:\t%s\n", green("\xE2\x96\xB6"), blue(cmrdr.GetCameraRTSPURL(stream)))
success++
} else {
fmt.Printf("%s\tAdmin panel URL:\t%s %s\n", red("\xE2\x96\xB6"), yellow(cmrdr.GetCameraAdminPanelURL(stream)), white("You can use this URL to try attacking the camera's admin panel instead."))
}
fmt.Printf("\tDevice model:\t\t%s\n\n", stream.Device)
if stream.Available {
fmt.Printf("\tAvailable:\t\t%s\n", green("yes"))
} else {
fmt.Printf("\tAvailable:\t\t%s\n", red("no"))
}
fmt.Printf("\tIP address:\t\t%s\n", stream.Address)
fmt.Printf("\tRTSP port:\t\t%d\n", stream.Port)
if stream.CredentialsFound {
fmt.Printf("\tUsername:\t\t%s\n", green(stream.Username))
fmt.Printf("\tPassword:\t\t%s\n", green(stream.Password))
} else {
fmt.Printf("\tUsername:\t\t%s\n", red("not found"))
fmt.Printf("\tPassword:\t\t%s\n", red("not found"))
}
if stream.RouteFound {
fmt.Printf("\tRTSP route:\t\t%s\n\n\n", green("/"+stream.Route))
} else {
fmt.Printf("\tRTSP route:\t\t%s\n\n\n", red("not found"))
}
}
if success > 1 {
fmt.Printf("%s Successful attack: %s devices were accessed", green("\xE2\x9C\x94"), green(len(streams)))
} else if success == 1 {
fmt.Printf("%s Successful attack: %s device was accessed", green("\xE2\x9C\x94"), green(len(streams)))
} else {
fmt.Printf("%s Streams were found but none were accessed. They are most likely configured with secure credentials and routes. You can try adding entries to the dictionary or generating your own in order to attempt a bruteforce attack on the cameras.\n", red("\xE2\x9C\x96"))
}
} else {
fmt.Printf("%s No streams were found. Please make sure that your target is on an accessible network.\n", red("\xE2\x9C\x96"))
}
}
func printErr(err error) {
red := color.New(color.FgRed, color.Bold).SprintFunc()
fmt.Printf("%s %v\n", red("\xE2\x9C\x96"), err)
os.Exit(1)
}
func updateSpinner(w *wow.Wow, text string, disabled bool) {
if !disabled {
w.Text(" " + text)
}
}
func startSpinner(disabled bool) *wow.Wow {
if !disabled {
w := wow.New(os.Stdout, spin.Get(spin.Dots), " Loading dictionaries...")
w.Start()
return w
}
return nil
}
// HACK: Waiting for a fix to issue
// https://github.com/gernest/wow/issues/5
func clearOutput(w *wow.Wow, disabled bool) {
if !disabled {
w.Text("\b")
time.Sleep(80 * time.Millisecond)
w.Stop()
}
}
+96
View File
@@ -0,0 +1,96 @@
package main
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/Ullaakut/cameradar/v5"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/disgo/style"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func parseArguments() error {
viper.SetEnvPrefix("cameradar")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
pflag.StringSliceP("targets", "t", []string{}, "The targets on which to scan for open RTSP streams - required (ex: 172.16.100.0/24)")
pflag.StringSliceP("ports", "p", []string{"554", "5554", "8554"}, "The ports on which to search for RTSP streams")
pflag.StringP("custom-routes", "r", "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/routes", "The path on which to load a custom routes dictionary")
pflag.StringP("custom-credentials", "c", "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json", "The path on which to load a custom credentials JSON dictionary")
pflag.IntP("scan-speed", "s", 4, "The nmap speed preset to use for scanning (lower is stealthier)")
pflag.DurationP("attack-interval", "I", 0, "The interval between each attack (i.e: 2000ms, higher is stealthier)")
pflag.DurationP("timeout", "T", 2000*time.Millisecond, "The timeout to use for attack attempts (i.e: 2000ms)")
pflag.BoolP("debug", "d", false, "Enable the debug logs")
pflag.BoolP("verbose", "v", false, "Enable the verbose logs")
pflag.BoolP("help", "h", false, "displays this help message")
viper.AutomaticEnv()
pflag.Parse()
err := viper.BindPFlags(pflag.CommandLine)
if err != nil {
return err
}
if viper.GetBool("help") {
pflag.Usage()
fmt.Println("\nExamples of usage:")
fmt.Println("\tScanning your home network for RTSP streams:\tcameradar -t 192.168.0.0/24")
fmt.Println("\tScanning a remote camera on a specific port:\tcameradar -t 172.178.10.14 -p 18554 -s 2")
fmt.Println("\tScanning an unstable remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 --timeout 10000 -l")
fmt.Println("\tStealthily scanning a remote network: \t\tcameradar -t 172.178.10.14/24 -s 1 -I 5000")
os.Exit(0)
}
if len(viper.GetStringSlice("targets")) == 0 {
pflag.Usage()
return errors.New("targets (-t, --targets) argument required\n examples:\n - 172.16.100.0/24\n - localhost\n - 8.8.8.8")
}
return nil
}
func main() {
err := parseArguments()
if err != nil {
printErr(err)
}
c, err := cameradar.New(
cameradar.WithTargets(viper.GetStringSlice("targets")),
cameradar.WithPorts(viper.GetStringSlice("ports")),
cameradar.WithDebug(viper.GetBool("debug")),
cameradar.WithVerbose(viper.GetBool("verbose")),
cameradar.WithCustomCredentials(viper.GetString("custom-credentials")),
cameradar.WithCustomRoutes(viper.GetString("custom-routes")),
cameradar.WithScanSpeed(viper.GetInt("scan-speed")),
cameradar.WithAttackInterval(viper.GetDuration("attack-interval")),
cameradar.WithTimeout(viper.GetDuration("timeout")),
)
if err != nil {
printErr(err)
}
scanResult, err := c.Scan()
if err != nil {
printErr(err)
}
streams, err := c.Attack(scanResult)
if err != nil {
printErr(err)
}
c.PrintStreams(streams)
}
func printErr(err error) {
disgo.Errorln(style.Failure(style.SymbolCross), err)
os.Exit(1)
}
+2 -2
View File
@@ -1,7 +1,7 @@
package cmrdr
package cameradar
import (
curl "github.com/andelf/go-curl"
curl "github.com/Ullaakut/go-curl"
)
// Curler is an interface that implements the CURL interface of the go-curl library
+2 -2
View File
@@ -1,10 +1,10 @@
package cmrdr
package cameradar
import (
"reflect"
"testing"
curl "github.com/andelf/go-curl"
curl "github.com/Ullaakut/go-curl"
)
func TestCurl(t *testing.T) {
+17 -2
View File
@@ -8,36 +8,51 @@
"admin1",
"administrator",
"Administrator",
"aiphone",
"Dinion",
"root",
"service",
"supervisor",
"ubnt"
],
"passwords" : [
"passwords": [
"",
"111111",
"1111111",
"1234",
"12345",
"123456",
"12345678",
"4321",
"666666",
"6fJjMKYx",
"888888",
"9999",
"admin",
"administrator",
"aiphone",
"camera",
"fliradmin",
"GRwvcj8j",
"hikvision",
"hikadmin",
"ikwd",
"jvc",
"kj3TqCWv",
"meinsm",
"pass",
"password",
"password123",
"reolink",
"root",
"service",
"supervisor",
"system",
"tlJwpbo6",
"toor",
"tp-link",
"ubnt",
"wbox123"
"wbox123",
"Y5eIMz3C"
]
}
+115 -37
View File
@@ -1,53 +1,127 @@
/live/ch01_0
0/1:1/main
0/usrnm:pwd/main
0/video1
1
1.AMP
1/h264major
1/stream1
11
12
125
1080p
1440p
480p
4K
666
720p
AVStream1_1
CAM_ID.password.mp2
CH001.sdp
GetData.cgi
HD
HighResolutionVideo
LowResolutionVideo
MediaInput/h264
MediaInput/mpeg4
ONVIF/MediaInput
ONVIF/MediaInput?profile=4_def_profile6
StdCh1
Streaming/Channels/1
Streaming/Unicast/channels/101
StreamingSetting?version=1.0&action=getRTSPStream&ChannelID=1&ChannelName=Channel1
VideoInput/1/h264/1
VideoInput/1/mpeg4/1
access_code
access_name_for_stream_1_to_5
api/mjpegvideo.cgi
av0_0
av2
avc
avn=2
axis-media/media.amp
axis-media/media.amp?camera=1
axis-media/media.amp?videocodec=h264
cam
cam/realmonitor
cam/realmonitor?channel=0&subtype=0
cam/realmonitor?channel=1&subtype=0
cam/realmonitor?channel=1&subtype=1
cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif
cam0
cam0_0
cam0_1
cam1
cam1/h264
cam1/h264/multicast
cam1/mjpeg
cam1/mpeg4
cam1/mpeg4?user='username'&pwd='password'
cam1/onvif-h264
camera.stm
ch0
ch00/0
ch001.sdp
ch01.264
ch01.264?
ch01.264?ptype=tcp
ch1_0
ch2_0
ch3_0
ch4_0
ch1/0
ch2/0
ch3/0
ch4/0
ch0_0.h264
ch0_unicast_firststream
ch0_unicast_secondstream
ch1-s1
channel1
gnz_media/main
h264
h264.sdp
h264/ch1/sub/av_stream
h264/media.amp
h264Preview_01_main
h264Preview_01_sub
h264_vga.sdp
h264_stream
image.mpg
img/media.sav
img/media.sav?channel=1
img/video.asf
img/video.sav
ioImage/1
ipcam.sdp
ipcam_h264.sdp
ipcam_mjpeg.sdp
live
live.sdp
live/av0
live/ch0
live/ch00_0
live/ch01_0
live/h264
live/main
live/main0
live/mpeg4
live1.sdp
live3.sdp
live_mpeg4.sdp
live_st1
livestream
livestream/
main
media
media.amp
media.amp?streamprofile=Profile1
media/media.amp
media/video1
medias2
mjpeg/media.smp
mp4
mpeg/media.amp
mpeg4
mpeg4/1/media.amp
mpeg4/media.amp
@@ -58,59 +132,63 @@ multicaststream
now.mp4
nph-h264.cgi
nphMpeg4/g726-640x
nphMpeg4/g726-640x48
nphMpeg4/g726-640x480
nphMpeg4/nil-320x240
onvif-media/media.amp
onvif1
pass@10.0.0.5:6667/blinkhd
play1.sdp
play2.sdp
profile2/media.smp
profile5/media.smp
rtpvideo1.sdp
rtsp_live0
rtsp_live1
rtsp_live2
rtsp_tunnel
rtsph264
rtsph2641080p
snap.jpg
stream
stream/0
stream/1
stream/live.sdp
stream.sdp
stream1
streaming/channels/0
streaming/channels/1
streaming/channels/101
tcp/av0_0
test
tmpfs/auto.jpg
trackID=1
ucast/11
udp/av0_0
udp/unicast/aiphone_H264
udpstream
user.pin.mp2
user=admin&password=&channel=1&stream=0.sdp?
user=admin&password=&channel=1&stream=0.sdp?real_stream
user=admin_password=?????_channel=1_stream=0.sdp?real_stream
user=admin_password=R5XFY888_channel=1_stream=0.sdp?real_stream
user_defined
v2
video
video.3gp
video.h264
video.mjpg
video.mp4
video.pro1
video.pro2
video.pro3
video0
video0.sdp
video1
video1.sdp
video1+audio1
videoMain
videoinput_1/h264_1/media.stm
videostream.asf
vis
wfov
video.h264
11
12
ch1-s1
live3.sdp
onvif-media/media.amp
axis-media/media.amp
axis-media/media.amp?videocodec=h264
mpeg4/media.amp
stream
cam/realmonitor
live
video.pro2
videoMain
VideoInput/1/mpeg4/1
VideoInput/1/h264/1
video.pro3
video.pro1
video.mjpg
h264_vga.sdp
media.amp
media
ONVIF/MediaInput
nphMpeg4/g726-640x48
MediaInput/mpeg4
MediaInput/h264
Streaming/Channels/1
ch0_0.h264
rtsph2641080p
live/av0
cam1/onvif-h264
ucast/11
LowResolutionVideo
1
live/ch00_0
medias2
+17
View File
@@ -0,0 +1,17 @@
module github.com/Ullaakut/cameradar/v5
go 1.14
require (
github.com/PuerkitoBio/goquery v1.5.0
github.com/Ullaakut/disgo v0.3.1
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd
github.com/Ullaakut/nmap v2.0.0+incompatible
github.com/VividCortex/ewma v1.1.1 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.4.0
github.com/stretchr/testify v1.2.2
github.com/vbauerster/mpb v3.4.0+incompatible
)
+163
View File
@@ -0,0 +1,163 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/Ullaakut/disgo v0.3.1 h1:BGGVHynji41KGuGI02ztTCnILRvyzlvmiCRl5bBpjKk=
github.com/Ullaakut/disgo v0.3.1/go.mod h1:/CSvpnYVSKOeh2dvUvx9cXshzz2t7T1/lRO/MrFj3fI=
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd h1:CMe+dX1CL4pCXNytxIB2U1qp0xZObGMZosJhaQdUlUo=
github.com/Ullaakut/go-curl v0.0.0-20190525093431-597e157bbffd/go.mod h1:u8mVgpDT88IPIt1B+Tu8vkrcFfBKGcfGwS9I7wmvMh0=
github.com/Ullaakut/nmap v2.0.0+incompatible h1:tNXub052dsnG8+yrgpph9nhVixIBdpRRgzvmQoc8eBA=
github.com/Ullaakut/nmap v2.0.0+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw=
github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+5 -5
View File
@@ -1,9 +1,9 @@
package cmrdr
package cameradar
import "fmt"
func replace(streams []Stream, new Stream) []Stream {
updatedSlice := streams[:0]
var updatedSlice []Stream
for _, old := range streams {
if old.Address == new.Address && old.Port == new.Port {
@@ -16,12 +16,12 @@ func replace(streams []Stream, new Stream) []Stream {
return updatedSlice
}
// GetCameraRTSPURL generates a stream's RTSP URL
// GetCameraRTSPURL generates a stream's RTSP URL.
func GetCameraRTSPURL(stream Stream) string {
return "rtsp://" + stream.Username + ":" + stream.Password + "@" + stream.Address + ":" + fmt.Sprint(stream.Port) + "/" + stream.Route
return "rtsp://" + stream.Username + ":" + stream.Password + "@" + stream.Address + ":" + fmt.Sprint(stream.Port) + "/" + stream.Route()
}
// GetCameraAdminPanelURL returns the URL to the camera's admin panel
// GetCameraAdminPanelURL returns the URL to the camera's admin panel.
func GetCameraAdminPanelURL(stream Stream) string {
return "http://" + stream.Address + "/"
}
+22 -28
View File
@@ -1,4 +1,4 @@
package cmrdr
package cameradar
import (
"testing"
@@ -10,25 +10,25 @@ func TestReplace(t *testing.T) {
validStream1 := Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
Port: 1,
}
validStream2 := Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
Port: 2,
}
invalidStreamNoPort := Stream{
invalidStream := Stream{
Device: "invalidDevice",
Address: "fakeAddress",
Port: 0,
Address: "anotherFakeAddress",
Port: 3,
}
invalidStreamNoPortModified := Stream{
invalidStreamModified := Stream{
Device: "updatedDevice",
Address: "fakeAddress",
Port: 1337,
Address: "anotherFakeAddress",
Port: 3,
}
testCases := []struct {
@@ -37,25 +37,21 @@ func TestReplace(t *testing.T) {
expectedStreams []Stream
}{
// Valid baseline
{
streams: []Stream{validStream1, validStream2, invalidStreamNoPort},
newStream: invalidStreamNoPortModified,
streams: []Stream{validStream1, validStream2, invalidStream},
newStream: invalidStreamModified,
expectedStreams: []Stream{validStream1, validStream2, invalidStreamNoPortModified},
expectedStreams: []Stream{validStream1, validStream2, invalidStreamModified},
},
}
for _, test := range testCases {
streams := replace(test.streams, test.newStream)
for _, stream := range test.streams {
foundStream := false
for _, result := range streams {
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
foundStream = true
}
}
assert.Equal(t, true, foundStream, "wrong streams parsed")
assert.Equal(t, len(test.expectedStreams), len(streams))
for _, expectedStream := range test.expectedStreams {
assert.Contains(t, streams, expectedStream)
}
}
}
@@ -65,7 +61,7 @@ func TestGetCameraRTSPURL(t *testing.T) {
Address: "1.2.3.4",
Username: "ullaakut",
Password: "ba69897483886f0d2b0afb6345b76c0c",
Route: "cameradar.sdp",
Routes: []string{"cameradar.sdp"},
Port: 1337,
}
@@ -74,16 +70,15 @@ func TestGetCameraRTSPURL(t *testing.T) {
expectedRTSPURL string
}{
// Valid baseline
{
stream: validStream,
expectedRTSPURL: "rtsp://ullaakut:ba69897483886f0d2b0afb6345b76c0c@1.2.3.4:1337/cameradar.sdp",
},
}
for _, test := range testCases {
output := GetCameraRTSPURL(test.stream)
assert.Equal(t, test.expectedRTSPURL, output, "wrong RTSP URL generated")
assert.Equal(t, test.expectedRTSPURL, GetCameraRTSPURL(test.stream))
}
}
@@ -97,15 +92,14 @@ func TestGetCameraAdminPanelURL(t *testing.T) {
expectedRTSPURL string
}{
// Valid baseline
{
stream: validStream,
expectedRTSPURL: "http://1.2.3.4/",
},
}
for _, test := range testCases {
output := GetCameraAdminPanelURL(test.stream)
assert.Equal(t, test.expectedRTSPURL, output, "wrong Admin Panel URL generated")
assert.Equal(t, test.expectedRTSPURL, GetCameraAdminPanelURL(test.stream))
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

+43 -30
View File
@@ -1,14 +1,13 @@
package cmrdr
package cameradar
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/pkg/errors"
)
var fs fileSystem = osFS{}
@@ -32,48 +31,51 @@ type osFS struct{}
func (osFS) Open(name string) (file, error) { return os.Open(name) }
func (osFS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
// LoadCredentials opens a dictionary file and returns its contents as a Credentials structure
func LoadCredentials(path string) (Credentials, error) {
var creds Credentials
// LoadCredentials opens a dictionary file and returns its contents as a Credentials structure.
func (s *Scanner) LoadCredentials() error {
s.term.Debugf("Loading credentials dictionary from path %q\n", s.credentialDictionaryPath)
// Open & Read XML file
content, err := ioutil.ReadFile(path)
// Open & Read XML file.
content, err := ioutil.ReadFile(s.credentialDictionaryPath)
if err != nil {
return creds, errors.Wrap(err, "could not read credentials dictionary file at "+path+":")
return fmt.Errorf("could not read credentials dictionary file at %q: %v", s.credentialDictionaryPath, err)
}
// Unmarshal content of JSON file into data structure
err = json.Unmarshal(content, &creds)
// Unmarshal content of JSON file into data structure.
err = json.Unmarshal(content, &s.credentials)
if err != nil {
return creds, err
return fmt.Errorf("unable to unmarshal dictionary contents: %v", err)
}
return creds, nil
s.term.Debugf("Loaded %d usernames and %d passwords\n", len(s.credentials.Usernames), len(s.credentials.Passwords))
return nil
}
// LoadRoutes opens a dictionary file and returns its contents as a Routes structure
func LoadRoutes(path string) (Routes, error) {
file, err := os.Open(path)
// LoadRoutes opens a dictionary file and returns its contents as a Routes structure.
func (s *Scanner) LoadRoutes() error {
s.term.Debugf("Loading routes dictionary from path %q\n", s.routeDictionaryPath)
file, err := os.Open(s.routeDictionaryPath)
if err != nil {
return nil, err
return fmt.Errorf("unable to open dictionary: %v", err)
}
defer file.Close()
var routes Routes
scanner := bufio.NewScanner(file)
for scanner.Scan() {
routes = append(routes, scanner.Text())
s.routes = append(s.routes, scanner.Text())
}
return routes, scanner.Err()
s.term.Debugf("Loaded %d routes\n", len(s.routes))
return scanner.Err()
}
// ParseCredentialsFromString parses a dictionary string and returns its contents as a Credentials structure
// ParseCredentialsFromString parses a dictionary string and returns its contents as a Credentials structure.
func ParseCredentialsFromString(content string) (Credentials, error) {
var creds Credentials
// Unmarshal content of JSON file into data structure
// Unmarshal content of JSON file into data structure.
err := json.Unmarshal([]byte(content), &creds)
if err != nil {
return creds, err
@@ -82,28 +84,39 @@ func ParseCredentialsFromString(content string) (Credentials, error) {
return creds, nil
}
// ParseRoutesFromString parses a dictionary string and returns its contents as a Routes structure
// ParseRoutesFromString parses a dictionary string and returns its contents as a Routes structure.
func ParseRoutesFromString(content string) Routes {
return strings.Split(content, "\n")
}
// ParseTargetsFile parses an input file containing hosts to targets
func ParseTargetsFile(path string) ([]string, error) {
// LoadTargets parses the file containing hosts to targets, if the targets are
// just set to a file name.
func (s *Scanner) LoadTargets() error {
if len(s.targets) != 1 {
return nil
}
path := s.targets[0]
_, err := fs.Stat(path)
if err != nil {
return []string{path}, nil
return nil
}
file, err := fs.Open(path)
if err != nil {
return []string{path}, err
return fmt.Errorf("unable to open targets file %q: %v", path, err)
}
defer file.Close()
bytes, err := ioutil.ReadAll(file)
if err != nil {
return []string{path}, err
return fmt.Errorf("unable to read targets file %q: %v", path, err)
}
return strings.Split(string(bytes), "\n"), nil
s.targets = strings.Split(string(bytes), "\n")
s.term.Debugf("Successfylly parsed targets file with %d entries", len(s.targets))
return nil
}
+177 -165
View File
@@ -1,12 +1,15 @@
package cmrdr
package cameradar
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/Ullaakut/disgo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
@@ -91,91 +94,79 @@ func TestLoadCredentials(t *testing.T) {
Passwords: []string{"12345", "root"},
}
testCases := []struct {
tests := []struct {
description string
input []byte
fileExists bool
expectedOutput Credentials
expectedErrMsg string
expectedCredentials Credentials
expectedErr error
}{
// Valid baseline
{
fileExists: true,
input: credentialsJSONString,
expectedOutput: validCredentials,
description: "Valid baseline",
fileExists: true,
input: credentialsJSONString,
expectedCredentials: validCredentials,
},
// File does not exist
{
fileExists: false,
input: credentialsJSONString,
expectedErrMsg: "could not read credentials dictionary file at",
description: "File does not exist",
fileExists: false,
input: credentialsJSONString,
expectedErr: errors.New("could not read credentials dictionary file at \"/tmp/cameradar_test_load_credentials_1.xml\": open /tmp/cameradar_test_load_credentials_1.xml: no such file or directory"),
},
// Invalid format
{
fileExists: true,
input: []byte("not json"),
expectedErrMsg: "invalid character",
description: "Invalid format",
fileExists: true,
input: []byte("not json"),
expectedErr: errors.New("unable to unmarshal dictionary contents: invalid character 'o' in literal null (expecting 'u')"),
},
// No streams in dictionary
{
description: "No streams in dictionary",
fileExists: true,
input: []byte("{\"invalid\":\"json\"}"),
},
}
for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
// create file
if test.fileExists {
_, err := os.Create(filePath)
if err != nil {
fmt.Printf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
err = ioutil.WriteFile(filePath, test.input, 0644)
if err != nil {
fmt.Printf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
}
result, err := LoadCredentials(filePath)
if len(test.expectedErrMsg) > 0 {
if err == nil {
fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1)
}
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, expectedUsername := range test.expectedOutput.Usernames {
foundUsername := false
for _, username := range result.Usernames {
if username == expectedUsername {
foundUsername = true
}
for i, test := range tests {
t.Run(test.description, func(t *testing.T) {
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
// create file.
if test.fileExists {
_, err := os.Create(filePath)
if err != nil {
t.Fatalf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
}
assert.Equal(t, true, foundUsername, "wrong usernames parsed")
}
for _, expectedPassword := range test.expectedOutput.Passwords {
foundPassword := false
for _, password := range result.Passwords {
if password == expectedPassword {
foundPassword = true
}
err = ioutil.WriteFile(filePath, test.input, 0644)
if err != nil {
t.Fatalf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
}
assert.Equal(t, true, foundPassword, "wrong passwords parsed")
}
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
credentialDictionaryPath: filePath,
}
err := scanner.LoadCredentials()
assert.Equal(t, test.expectedErr, err)
assert.Len(t, scanner.credentials.Usernames, len(test.expectedCredentials.Usernames))
for _, expectedUsername := range test.expectedCredentials.Usernames {
assert.Contains(t, scanner.credentials.Usernames, expectedUsername)
}
assert.Len(t, scanner.credentials.Passwords, len(test.expectedCredentials.Passwords))
for _, expectedPassword := range test.expectedCredentials.Passwords {
assert.Contains(t, scanner.credentials.Passwords, expectedPassword)
}
})
}
}
@@ -183,74 +174,69 @@ func TestLoadRoutes(t *testing.T) {
routesJSONString := []byte("admin\nroot")
validRoutes := Routes{"admin", "root"}
testCases := []struct {
input []byte
fileExists bool
tests := []struct {
description string
input []byte
fileExists bool
expectedOutput Routes
expectedErrMsg string
expectedRoutes Routes
expectedErr error
}{
// Valid baseline
{
description: "Valid baseline",
fileExists: true,
input: routesJSONString,
expectedOutput: validRoutes,
expectedRoutes: validRoutes,
},
// File does not exist
{
fileExists: false,
input: routesJSONString,
expectedErrMsg: "no such file or directory",
description: "File does not exist",
fileExists: false,
input: routesJSONString,
expectedErr: errors.New("unable to open dictionary: open /tmp/cameradar_test_load_routes_1.xml: no such file or directory"),
},
// No streams in dictionary
{
description: "No streams in dictionary",
fileExists: true,
input: []byte(""),
},
}
for i, test := range testCases {
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
for i, test := range tests {
t.Run(test.description, func(t *testing.T) {
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
// create file
if test.fileExists {
_, err := os.Create(filePath)
if err != nil {
fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
err = ioutil.WriteFile(filePath, test.input, 0644)
if err != nil {
fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
}
result, err := LoadRoutes(filePath)
if len(test.expectedErrMsg) > 0 {
if err == nil {
fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
os.Exit(1)
}
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
} else {
if err != nil {
fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
os.Exit(1)
}
for _, expectedRoute := range test.expectedOutput {
foundRoute := false
for _, route := range result {
if route == expectedRoute {
foundRoute = true
}
// Create file.
if test.fileExists {
_, err := os.Create(filePath)
if err != nil {
fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
assert.Equal(t, true, foundRoute, "wrong routes parsed")
err = ioutil.WriteFile(filePath, test.input, 0644)
if err != nil {
fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
os.Exit(1)
}
}
}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
routeDictionaryPath: filePath,
}
err := scanner.LoadRoutes()
assert.Equal(t, test.expectedErr, err)
assert.Len(t, scanner.routes, len(test.expectedRoutes))
for _, expectedRoute := range test.expectedRoutes {
assert.Contains(t, scanner.routes, expectedRoute)
}
})
}
}
@@ -295,58 +281,59 @@ func TestParseCredentialsFromString(t *testing.T) {
},
}
testCases := []struct {
str string
expectedResult Credentials
tests := []struct {
str string
expectedCredentials Credentials
}{
{
str: "{\"usernames\":[\"\",\"admin\",\"Admin\",\"Administrator\",\"root\",\"supervisor\",\"ubnt\",\"service\",\"Dinion\",\"administrator\",\"admin1\"],\"passwords\":[\"\",\"admin\",\"9999\",\"123456\",\"pass\",\"camera\",\"1234\",\"12345\",\"fliradmin\",\"system\",\"jvc\",\"meinsm\",\"root\",\"4321\",\"111111\",\"1111111\",\"password\",\"ikwd\",\"supervisor\",\"ubnt\",\"wbox123\",\"service\"]}",
expectedResult: defaultCredentials,
str: "{\"usernames\":[\"\",\"admin\",\"Admin\",\"Administrator\",\"root\",\"supervisor\",\"ubnt\",\"service\",\"Dinion\",\"administrator\",\"admin1\"],\"passwords\":[\"\",\"admin\",\"9999\",\"123456\",\"pass\",\"camera\",\"1234\",\"12345\",\"fliradmin\",\"system\",\"jvc\",\"meinsm\",\"root\",\"4321\",\"111111\",\"1111111\",\"password\",\"ikwd\",\"supervisor\",\"ubnt\",\"wbox123\",\"service\"]}",
expectedCredentials: defaultCredentials,
},
{
str: "{}",
expectedResult: Credentials{},
str: "{}",
expectedCredentials: Credentials{},
},
{
str: "{\"invalid_field\":42}",
expectedResult: Credentials{},
str: "{\"invalid_field\":42}",
expectedCredentials: Credentials{},
},
{
str: "not json",
expectedResult: Credentials{},
str: "not json",
expectedCredentials: Credentials{},
},
}
for _, test := range testCases {
for _, test := range tests {
parsedCredentials, _ := ParseCredentialsFromString(test.str)
assert.Equal(t, test.expectedResult, parsedCredentials, "unexpected result, parse error")
assert.Equal(t, test.expectedCredentials, parsedCredentials)
}
}
func TestParseRoutesFromString(t *testing.T) {
testCases := []struct {
tests := []struct {
str string
expectedResult Routes
expectedRoutes Routes
}{
{
str: "a\nb\nc",
expectedResult: []string{"a", "b", "c"},
expectedRoutes: []string{"a", "b", "c"},
},
{
str: "a",
expectedResult: []string{"a"},
expectedRoutes: []string{"a"},
},
{
str: "",
expectedResult: []string{""},
expectedRoutes: []string{""},
},
}
for _, test := range testCases {
parsedRoutes := ParseRoutesFromString(test.str)
assert.Equal(t, test.expectedResult, parsedRoutes, "unexpected result, parse error")
for _, test := range tests {
assert.Equal(t, test.expectedRoutes, ParseRoutesFromString(test.str))
}
}
func TestParseTargetsFile(t *testing.T) {
func TestLoadTargets(t *testing.T) {
oldFS := fs
mfs := &mockedFS{}
@@ -355,65 +342,90 @@ func TestParseTargetsFile(t *testing.T) {
fs = oldFS
}()
testCases := []struct {
input string
tests := []struct {
description string
targets []string
fileExists bool
openError bool
readError bool
expectedResult []string
expectedError error
expectedTargets []string
expectedError error
}{
{
input: "0.0.0.0",
description: "not a file",
targets: []string{"0.0.0.0"},
fileExists: false,
expectedResult: []string{"0.0.0.0"},
expectedError: nil,
expectedTargets: []string{"0.0.0.0"},
expectedError: nil,
},
{
input: "test_does_not_really_exist",
description: "not file targets",
targets: []string{"0.0.0.0", "1.2.3.4/24"},
expectedTargets: []string{"0.0.0.0", "1.2.3.4/24"},
expectedError: nil,
},
{
description: "file contains targets",
targets: []string{"test_does_not_really_exist"},
fileExists: true,
expectedResult: []string{"0.0.0.0", "localhost", "192.17.0.0/16", "192.168.1.140-255", "192.168.2-3.0-255"},
expectedError: nil,
expectedTargets: []string{"0.0.0.0", "localhost", "192.17.0.0/16", "192.168.1.140-255", "192.168.2-3.0-255"},
expectedError: nil,
},
{
input: "test_does_not_really_exist",
description: "open error",
targets: []string{"test_does_not_really_exist"},
fileExists: true,
openError: true,
expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist,
expectedTargets: []string{"test_does_not_really_exist"},
expectedError: errors.New("unable to open targets file \"test_does_not_really_exist\": file does not exist"),
},
{
input: "test_does_not_really_exist",
description: "read error",
targets: []string{"test_does_not_really_exist"},
fileExists: true,
readError: true,
expectedResult: []string{"test_does_not_really_exist"},
expectedError: os.ErrNotExist,
expectedTargets: []string{"test_does_not_really_exist"},
expectedError: errors.New("unable to read targets file \"test_does_not_really_exist\": file does not exist"),
},
}
for _, test := range testCases {
mfs.fileExists = test.fileExists
mfs.openError = test.openError
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
mfs.fileExists = test.fileExists
mfs.openError = test.openError
mfs.fileMock = &fileMock{
readError: test.readError,
}
mfs.fileMock.On("Close").Return(nil)
mfs.fileMock.WriteString("0.0.0.0\nlocalhost\n192.17.0.0/16\n192.168.1.140-255\n192.168.2-3.0-255")
mfs.fileMock = &fileMock{
readError: test.readError,
}
mfs.fileMock.On("Close").Return(nil)
mfs.fileMock.WriteString("0.0.0.0\nlocalhost\n192.17.0.0/16\n192.168.1.140-255\n192.168.2-3.0-255")
result, err := ParseTargetsFile(test.input)
assert.Equal(t, test.expectedResult, result, "unexpected result, parse error")
assert.Equal(t, test.expectedError, err, "unexpected error")
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
targets: test.targets,
}
err := scanner.LoadTargets()
assert.Equal(t, test.expectedTargets, scanner.targets)
assert.Equal(t, test.expectedError, err)
})
}
}
+17 -7
View File
@@ -1,19 +1,29 @@
package cmrdr
package cameradar
import "time"
// Stream represents a camera's RTSP stream
type Stream struct {
Device string `json:"device"`
Username string `json:"username"`
Password string `json:"password"`
Route string `json:"route"`
Address string `json:"address" validate:"required"`
Port uint16 `json:"port" validate:"required"`
Device string `json:"device"`
Username string `json:"username"`
Password string `json:"password"`
Routes []string `json:"route"`
Address string `json:"address" validate:"required"`
Port uint16 `json:"port" validate:"required"`
CredentialsFound bool `json:"credentials_found"`
RouteFound bool `json:"route_found"`
Available bool `json:"available"`
AuthenticationType int `json:"authentication_type"`
}
// Route returns this stream's route if there is one.
func (s Stream) Route() string {
if len(s.Routes) > 0 {
return s.Routes[0]
}
return ""
}
// Credentials is a map of credentials
+25 -14
View File
@@ -1,4 +1,4 @@
package cmrdr
package cameradar
import (
"strings"
@@ -6,7 +6,7 @@ import (
"github.com/Ullaakut/nmap"
)
// Discover scans the target networks and tries to find RTSP streams within them.
// Scan scans the target networks and tries to find RTSP streams within them.
//
// targets can be:
//
@@ -18,28 +18,35 @@ import (
// ports can be:
//
// - one or multiple ports and port ranges separated by commas (e.g.: 554,8554-8560,18554-28554)
func Discover(targets, ports []string, speed int) ([]Stream, error) {
// Run nmap command to discover open ports on the specified targets & ports
scanner, err := nmap.NewScanner(
nmap.WithTargets(targets...),
nmap.WithPorts(ports...),
nmap.WithTimingTemplate(nmap.Timing(speed)),
func (s *Scanner) Scan() ([]Stream, error) {
s.term.StartStep("Scanning the network")
// Run nmap command to discover open ports on the specified targets & ports.
nmapScanner, err := nmap.NewScanner(
nmap.WithTargets(s.targets...),
nmap.WithPorts(s.ports...),
nmap.WithServiceInfo(),
nmap.WithTimingTemplate(nmap.Timing(s.scanSpeed)),
)
if err != nil {
return nil, err
return nil, s.term.FailStepf("unable to create network scanner: %v", err)
}
return scan(scanner)
return s.scan(nmapScanner)
}
func scan(scanner nmap.ScanRunner) ([]Stream, error) {
results, err := scanner.Run()
func (s *Scanner) scan(nmapScanner nmap.ScanRunner) ([]Stream, error) {
results, warnings, err := nmapScanner.Run()
if err != nil {
return nil, err
return nil, s.term.FailStepf("error while scanning network: %v", err)
}
for _, warning := range warnings {
s.term.Infoln("[Nmap Warning]", warning)
}
// Get streams from nmap results.
var streams []Stream
// Get streams from nmap results
for _, host := range results.Hosts {
for _, port := range host.Ports {
if port.Status() != "open" {
@@ -60,5 +67,9 @@ func scan(scanner nmap.ScanRunner) ([]Stream, error) {
}
}
s.term.Debugf("Found %d RTSP streams\n", len(streams))
s.term.EndStep()
return streams, nil
}
+63 -44
View File
@@ -1,10 +1,13 @@
package cmrdr
package cameradar
import (
"errors"
"io/ioutil"
"os"
"testing"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/nmap"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -14,16 +17,42 @@ type nmapMock struct {
mock.Mock
}
func (m *nmapMock) Run() (*nmap.Run, error) {
func (m *nmapMock) Run() (*nmap.Run, []string, error) {
args := m.Called()
if args.Get(0) != nil {
return args.Get(0).(*nmap.Run), args.Error(1)
if args.Get(0) != nil && args.Get(1) != nil {
return args.Get(0).(*nmap.Run), args.Get(1).([]string), args.Error(2)
}
return nil, args.Error(1)
return nil, nil, args.Error(2)
}
func TestDiscover(t *testing.T) {
var (
validStream1 = Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
}
validStream2 = Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
}
invalidStreamNoPort = Stream{
Device: "invalidDevice",
Address: "fakeAddress",
Port: 0,
}
invalidStreamNoAddress = Stream{
Device: "invalidDevice",
Address: "",
Port: 1337,
}
)
func TestScan(t *testing.T) {
tests := []struct {
description string
@@ -32,8 +61,8 @@ func TestDiscover(t *testing.T) {
speed int
removePath bool
expectedErr error
expectedResult []Stream
expectedErr error
expectedStreams []Stream
}{
{
description: "create new scanner and call scan, no error",
@@ -48,7 +77,7 @@ func TestDiscover(t *testing.T) {
removePath: true,
ports: []string{"80"},
expectedErr: errors.New("'nmap' binary was not found"),
expectedErr: errors.New("unable to create network scanner: nmap binary was not found"),
},
}
@@ -58,43 +87,28 @@ func TestDiscover(t *testing.T) {
os.Setenv("PATH", "")
}
result, err := Discover(test.targets, test.ports, test.speed)
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
targets: test.targets,
ports: test.ports,
scanSpeed: test.speed,
}
result, err := scanner.Scan()
assert.Equal(t, test.expectedErr, err)
assert.Equal(t, test.expectedResult, result)
assert.Equal(t, test.expectedStreams, result)
})
}
}
func TestScan(t *testing.T) {
validStream1 := Stream{
Device: "fakeDevice",
Address: "fakeAddress",
Port: 1337,
}
validStream2 := Stream{
Device: "fakeDevice",
Address: "differentFakeAddress",
Port: 1337,
}
invalidStreamNoPort := Stream{
Device: "invalidDevice",
Address: "fakeAddress",
Port: 0,
}
invalidStreamNoAddress := Stream{
Device: "invalidDevice",
Address: "",
Port: 1337,
}
testCases := []struct {
func TestInternalScan(t *testing.T) {
tests := []struct {
description string
nmapResult *nmap.Run
nmapError error
nmapResult *nmap.Run
nmapWarnings []string
nmapError error
expectedStreams []Stream
expectedErr error
@@ -280,18 +294,23 @@ func TestScan(t *testing.T) {
{
description: "scan failed",
nmapError: errors.New("scan failed"),
expectedErr: errors.New("scan failed"),
nmapError: errors.New("scan failed"),
nmapWarnings: []string{"invalid host"},
expectedErr: errors.New("error while scanning network: scan failed"),
},
}
for _, test := range testCases {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
nmapMock := &nmapMock{}
nmapMock.On("Run").Return(test.nmapResult, test.nmapError)
nmapMock.On("Run").Return(test.nmapResult, test.nmapWarnings, test.nmapError)
results, err := scan(nmapMock)
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)),
}
results, err := scanner.scan(nmapMock)
assert.Equal(t, test.expectedErr, err)
assert.Equal(t, test.expectedStreams, results, "wrong streams parsed")
+162
View File
@@ -0,0 +1,162 @@
package cameradar
import (
"fmt"
"os"
"time"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/disgo/style"
curl "github.com/Ullaakut/go-curl"
)
const (
defaultCredentialDictionaryPath = "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/credentials.json"
defaultRouteDictionaryPath = "${GOPATH}/src/github.com/Ullaakut/cameradar/dictionaries/routes"
)
// Scanner represents a cameradar scanner. It scans a network and
// attacks all streams found to get their RTSP credentials.
type Scanner struct {
curl Curler
term *disgo.Terminal
targets []string
ports []string
debug bool
verbose bool
scanSpeed int
attackInterval time.Duration
timeout time.Duration
credentialDictionaryPath string
routeDictionaryPath string
credentials Credentials
routes Routes
}
// New creates a new Cameradar Scanner and applies the given options.
func New(options ...func(*Scanner)) (*Scanner, error) {
err := curl.GlobalInit(curl.GLOBAL_ALL)
if err != nil {
return nil, fmt.Errorf("unable to initialize curl library: %v", err)
}
handle := curl.EasyInit()
if handle == nil {
return nil, fmt.Errorf("unable to initialize curl handle: %v", err)
}
scanner := &Scanner{
curl: &Curl{CURL: handle},
credentialDictionaryPath: defaultCredentialDictionaryPath,
routeDictionaryPath: defaultRouteDictionaryPath,
}
for _, option := range options {
option(scanner)
}
gopath := os.Getenv("GOPATH")
if gopath == "" && scanner.credentialDictionaryPath == defaultCredentialDictionaryPath && scanner.routeDictionaryPath == defaultRouteDictionaryPath {
disgo.Errorln(style.Failure("No $GOPATH was found.\nDictionaries may not be loaded properly, please set your $GOPATH to use the default dictionaries."))
}
scanner.credentialDictionaryPath = os.ExpandEnv(scanner.credentialDictionaryPath)
scanner.routeDictionaryPath = os.ExpandEnv(scanner.routeDictionaryPath)
scanner.term = disgo.NewTerminal(
disgo.WithDebug(scanner.debug),
)
err = scanner.LoadTargets()
if err != nil {
return nil, fmt.Errorf("unable to parse target file: %v", err)
}
scanner.term.StartStepf("Loading credentials")
err = scanner.LoadCredentials()
if err != nil {
return nil, scanner.term.FailStepf("unable to load credentials dictionary: %v", err)
}
scanner.term.StartStepf("Loading routes")
err = scanner.LoadRoutes()
if err != nil {
return nil, scanner.term.FailStepf("unable to load credentials dictionary: %v", err)
}
disgo.EndStep()
return scanner, nil
}
// WithTargets specifies the targets to scan and attack.
func WithTargets(targets []string) func(s *Scanner) {
return func(s *Scanner) {
s.targets = targets
}
}
// WithPorts specifies the ports to scan and attack.
func WithPorts(ports []string) func(s *Scanner) {
return func(s *Scanner) {
s.ports = ports
}
}
// WithDebug specifies whether or not to enable debug logs.
func WithDebug(debug bool) func(s *Scanner) {
return func(s *Scanner) {
s.debug = debug
}
}
// WithVerbose specifies whether or not to enable verbose logs.
func WithVerbose(verbose bool) func(s *Scanner) {
return func(s *Scanner) {
s.verbose = verbose
}
}
// WithCustomCredentials specifies a custom credential dictionary
// to use for the attacks.
func WithCustomCredentials(dictionaryPath string) func(s *Scanner) {
return func(s *Scanner) {
s.credentialDictionaryPath = dictionaryPath
}
}
// WithCustomRoutes specifies a custom route dictionary
// to use for the attacks.
func WithCustomRoutes(dictionaryPath string) func(s *Scanner) {
return func(s *Scanner) {
s.routeDictionaryPath = dictionaryPath
}
}
// WithScanSpeed specifies the speed at which the scan should be executed. Faster
// means easier to detect, slower has bigger timeout values and is more silent.
func WithScanSpeed(speed int) func(s *Scanner) {
return func(s *Scanner) {
s.scanSpeed = speed
}
}
// WithAttackInterval specifies the interval of time during which Cameradar
// should wait between each attack attempt during bruteforcing.
// Setting a high value for this obviously makes attacks much slower.
func WithAttackInterval(interval time.Duration) func(s *Scanner) {
return func(s *Scanner) {
s.attackInterval = interval
}
}
// WithTimeout specifies the amount of time after which attack requests should
// timeout. This should be high if the network you are attacking has a poor
// connectivity or that you are located far away from it.
func WithTimeout(timeout time.Duration) func(s *Scanner) {
return func(s *Scanner) {
s.timeout = timeout
}
}
+150
View File
@@ -0,0 +1,150 @@
package cameradar
import (
"fmt"
"io/ioutil"
"os"
"testing"
"time"
curl "github.com/Ullaakut/go-curl"
"github.com/stretchr/testify/assert"
)
func TestNew(t *testing.T) {
tests := []struct {
description string
targets []string
ports []string
debug bool
verbose bool
customCredentials string
customRoutes string
speed int
attackInterval time.Duration
timeout time.Duration
loadTargetsFail bool
loadCredsFail bool
loadRoutesFail bool
curlGlobalFail bool
curlEasyFail bool
expectedErr bool
}{
{
description: "no error while loading dictionaries",
targets: []string{"titi", "toto"},
ports: []string{"554"},
debug: true,
verbose: false,
speed: 3,
timeout: time.Millisecond,
},
{
description: "unable to load targets",
loadTargetsFail: true,
expectedErr: true,
},
{
description: "unable to load credentials",
loadCredsFail: true,
expectedErr: true,
},
{
description: "unable to load routes",
loadRoutesFail: true,
expectedErr: true,
},
{
description: "curl fails to init",
curlGlobalFail: true,
expectedErr: true,
},
{
description: "curl fails to create handle",
curlEasyFail: true,
expectedErr: true,
},
{
description: "gopath not set and default dicts",
customCredentials: defaultCredentialDictionaryPath,
customRoutes: defaultRouteDictionaryPath,
expectedErr: true,
},
}
// Temporarily empty the gopath for testing purposes.
defer os.Setenv("GOPATH", os.Getenv("GOPATH"))
for i, test := range tests {
t.Run(test.description, func(t *testing.T) {
os.Setenv("GOPATH", "")
if test.loadTargetsFail {
test.targets = []string{generateTmpFileName(i, "targets")}
ioutil.WriteFile(test.targets[0], []byte(`0.0.0.0`), 0000)
}
if !test.loadCredsFail && test.customCredentials == "" {
test.customCredentials = generateTmpFileName(i, "creds")
ioutil.WriteFile(test.customCredentials, []byte(`{"usernames":["admin"],"passwords":["admin"]}`), 0644)
}
if !test.loadRoutesFail && test.customRoutes == "" {
test.customRoutes = generateTmpFileName(i, "routes")
ioutil.WriteFile(test.customRoutes, []byte(`live.sdp`), 0644)
}
curl.TestGlobalFail = test.curlGlobalFail
curl.TestEasyFail = test.curlEasyFail
scanner, err := New(
WithTargets(test.targets),
WithPorts(test.ports),
WithDebug(test.debug),
WithVerbose(test.verbose),
WithScanSpeed(test.speed),
WithAttackInterval(test.attackInterval),
WithTimeout(test.timeout),
WithCustomCredentials(test.customCredentials),
WithCustomRoutes(test.customRoutes),
)
if test.expectedErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
if scanner != nil {
assert.Equal(t, test.targets, scanner.targets)
assert.Equal(t, test.ports, scanner.ports)
assert.Equal(t, test.debug, scanner.debug)
assert.Equal(t, test.verbose, scanner.verbose)
assert.Equal(t, test.speed, scanner.scanSpeed)
assert.Equal(t, test.attackInterval, scanner.attackInterval)
assert.Equal(t, test.timeout, scanner.timeout)
}
})
}
}
func generateTmpFileName(iteration int, purpose string) string {
return fmt.Sprintf("/tmp/cameradar_test_scanner_%s_%d_%d", purpose, time.Now().Unix(), iteration)
}
+68
View File
@@ -0,0 +1,68 @@
package cameradar
import (
"github.com/Ullaakut/disgo/style"
curl "github.com/Ullaakut/go-curl"
)
// PrintStreams prints information on each stream.
func (s *Scanner) PrintStreams(streams []Stream) {
if len(streams) == 0 {
s.term.Infof("%s No streams were found. Please make sure that your target is on an accessible network.\n", style.Failure(style.SymbolCross))
}
success := 0
for _, stream := range streams {
if stream.Available {
s.term.Infof("%s\tDevice RTSP URL:\t%s\n", style.Success(style.SymbolRightTriangle), style.Link(GetCameraRTSPURL(stream)))
s.term.Infof("\tAvailable:\t\t%s\n", style.Success(style.SymbolCheck))
success++
} else {
s.term.Infof("%s\tAdmin panel URL:\t%s You can use this URL to try attacking the camera's admin panel instead.\n", style.Failure(style.SymbolCross), style.Link(GetCameraAdminPanelURL(stream)))
s.term.Infof("\tAvailable:\t\t%s\n", style.Failure(style.SymbolCross))
}
if len(stream.Device) > 0 {
s.term.Infof("\tDevice model:\t\t%s\n\n", stream.Device)
}
s.term.Infof("\tIP address:\t\t%s\n", stream.Address)
s.term.Infof("\tRTSP port:\t\t%d\n", stream.Port)
switch stream.AuthenticationType {
case curl.AUTH_NONE:
s.term.Infoln("\tThis camera does not require authentication")
case curl.AUTH_BASIC:
s.term.Infoln("\tAuth type:\t\tbasic")
case curl.AUTH_DIGEST:
s.term.Infoln("\tAuth type:\t\tdigest")
}
if stream.CredentialsFound {
s.term.Infof("\tUsername:\t\t%s\n", style.Success(stream.Username))
s.term.Infof("\tPassword:\t\t%s\n", style.Success(stream.Password))
} else {
s.term.Infof("\tUsername:\t\t%s\n", style.Failure("not found"))
s.term.Infof("\tPassword:\t\t%s\n", style.Failure("not found"))
}
s.term.Infoln("\tRTSP routes:")
if stream.RouteFound {
for _, route := range stream.Routes {
s.term.Infoln(style.Success("\t\t\t\t/" + route))
}
} else {
s.term.Infoln(style.Failure("not found"))
}
s.term.Info("\n\n")
}
if success > 1 {
s.term.Infof("%s Successful attack: %s devices were accessed", style.Success(style.SymbolCheck), style.Success(len(streams)))
} else if success == 1 {
s.term.Infof("%s Successful attack: %s device was accessed", style.Success(style.SymbolCheck), style.Success("one"))
} else {
s.term.Infof("%s Streams were found but none were accessed. They are most likely configured with secure credentials and routes. You can try adding entries to the dictionary or generating your own in order to attempt a bruteforce attack on the cameras.\n", style.Failure("\xE2\x9C\x96"))
}
}
+186
View File
@@ -0,0 +1,186 @@
package cameradar
import (
"bytes"
"testing"
"github.com/Ullaakut/disgo"
"github.com/stretchr/testify/assert"
)
var (
unavailable = Stream{}
available = Stream{
Available: true,
}
deviceFound = Stream{
Device: "devicename",
}
noAuth = Stream{
AuthenticationType: 0,
}
basic = Stream{
AuthenticationType: 1,
}
digest = Stream{
AuthenticationType: 2,
}
credsFound = Stream{
CredentialsFound: true,
Username: "us3r",
Password: "p4ss",
}
routeFound = Stream{
RouteFound: true,
Routes: []string{"r0ute"},
}
)
func TestPrintStreams(t *testing.T) {
tests := []struct {
description string
streams []Stream
expectedLogs []string
}{
{
description: "displays the proper message when no streams found",
streams: nil,
expectedLogs: []string{"No streams were found"},
},
{
description: "displays the admin panel URL when a stream is not accessible",
streams: []Stream{
unavailable,
},
expectedLogs: []string{"Admin panel URL"},
},
{
description: "displays the device name when it is found",
streams: []Stream{
deviceFound,
},
expectedLogs: []string{"Device model:"},
},
{
description: "displays authentication type (no auth)",
streams: []Stream{
noAuth,
},
expectedLogs: []string{"This camera does not require authentication"},
},
{
description: "displays authentication type (basic)",
streams: []Stream{
basic,
},
expectedLogs: []string{"basic"},
},
{
description: "displays authentication type (digest)",
streams: []Stream{
digest,
},
expectedLogs: []string{"digest"},
},
{
description: "displays credentials properly",
streams: []Stream{
credsFound,
},
expectedLogs: []string{
"Username",
"us3r",
"Password",
"p4ss",
},
},
{
description: "displays route properly",
streams: []Stream{
routeFound,
},
expectedLogs: []string{
"RTSP route",
"/r0ute",
},
},
{
description: "displays successes properly (no success)",
streams: []Stream{
unavailable,
},
expectedLogs: []string{
"Streams were found but none were accessed",
},
},
{
description: "displays successes properly (1 success)",
streams: []Stream{
available,
},
expectedLogs: []string{
"Successful attack",
"device was accessed",
},
},
{
description: "displays successes properly (multiple successes)",
streams: []Stream{
available,
available,
available,
available,
},
expectedLogs: []string{
"Successful attack",
"devices were accessed",
},
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
writer := &bytes.Buffer{}
scanner := &Scanner{
term: disgo.NewTerminal(disgo.WithDefaultOutput(writer)),
}
scanner.PrintStreams(test.streams)
for _, expectedLog := range test.expectedLogs {
assert.Contains(t, writer.String(), expectedLog)
}
})
}
}
+191
View File
@@ -0,0 +1,191 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"sort"
"strings"
"sync"
"github.com/Ullaakut/disgo/style"
"github.com/PuerkitoBio/goquery"
"github.com/Ullaakut/disgo"
"github.com/vbauerster/mpb"
"github.com/vbauerster/mpb/decor"
)
const dictionaryURL = "https://community.geniusvision.net/platform/cprndr/manulist"
var rtspURLsFound sync.Map
func main() {
if err := updateDictionary(); err != nil {
log.Fatalf(err.Error())
}
}
func updateDictionary() error {
disgo.SetTerminalOptions(disgo.WithColors(true), disgo.WithDebug(true))
disgo.StartStep("Fetching dictionary list")
resp, err := http.Get(dictionaryURL)
if err != nil {
return disgo.FailStepf("unable to download dictionaries: %v", err)
}
defer resp.Body.Close()
disgo.StartStep("Parsing dictionary list")
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return disgo.FailStepf("unable to read from dictionary list: %v", err)
}
var vendorURLs []string
doc.Find("td.simpletable a").Each(func(i int, s *goquery.Selection) {
url, ok := s.Attr("href")
if !ok {
return
}
if url != "javascript:void(0)" {
vendorURLs = append(vendorURLs, url)
}
})
disgo.StartStep("Loading current cameradar dictionary")
currentDictionary, err := ioutil.ReadFile("dictionaries/routes")
if err != nil {
return disgo.FailStepf("unable to read current dictionary: %v", err)
}
dictionaryEntries := bytes.Split(currentDictionary, []byte("\n"))
for _, rtspURL := range dictionaryEntries {
rtspURLsFound.Store(string(rtspURL), struct{}{})
}
disgo.Debugf("Current dictionary has %d entries\n", len(dictionaryEntries))
disgo.EndStep()
p := mpb.New(mpb.WithWidth(64))
name := fmt.Sprintf("Fetching default routes from %d constructors:", len(vendorURLs))
bar := p.AddBar(int64(len(vendorURLs)),
// set custom bar style, default one is "[=>-]"
mpb.BarStyle("╢▌▌░╟"),
mpb.PrependDecorators(
// display our name with one space on the right
decor.Name(name, decor.WC{W: len(name), C: decor.DidentRight}),
),
mpb.AppendDecorators(decor.Percentage()),
)
for _, url := range vendorURLs {
go loadRoutes(url, bar)
}
p.Wait()
disgo.StartStep("Converting found routes into proper data model")
var rtspURLs []string
rtspURLsFound.Range(func(rtspURL, _ interface{}) bool {
disgo.Infoln("Adding URL", rtspURL.(string))
rtspURLs = append(rtspURLs, rtspURL.(string))
return true
})
sort.Slice(rtspURLs, func(a, b int) bool {
return rtspURLs[a] < rtspURLs[b]
})
disgo.EndStep()
if len(dictionaryEntries) < len(rtspURLs) {
disgo.Infof("%s Saving them in cameradar default dictionary.\n", style.Success("Found ", len(rtspURLs)-len(dictionaryEntries), " new entries!"))
saveRoutes(rtspURLs)
} else {
disgo.Infoln(style.Success("No new entry found, dictionary up-to-date! :)"))
}
return nil
}
func loadRoutes(url string, bar *mpb.Bar) {
defer bar.IncrBy(1)
var (
failureCounter int
resp *http.Response
err error
)
for failureCounter < 5 {
resp, err = http.Get(url)
if err != nil {
failureCounter++
} else {
break
}
}
if failureCounter == 5 {
disgo.Errorln("Request failed 5 times in a row, giving up on this vendor")
return
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
disgo.Errorf("unable to read from dictionary list for URL %q: %v\n", url, err)
return
}
doc.Find("tr.simpletable td.simpletable:nth-child(4) a").Each(func(i int, s *goquery.Selection) {
rtspURL := s.Text()
if strings.HasPrefix(rtspURL, "(") && strings.HasSuffix(rtspURL, ")") {
return
}
if strings.HasPrefix(rtspURL, "[") && strings.HasSuffix(rtspURL, "]") {
return
}
if strings.HasPrefix(rtspURL, "http://") {
return
}
// Skip the port and only get the route.
if strings.HasPrefix(rtspURL, "rtsp://ip-addr:") {
routeAndPort := strings.TrimSpace(strings.TrimPrefix(rtspURL, "rtsp://ip-addr:"))
route := strings.TrimLeft(routeAndPort, "0123456789/")
rtspURLsFound.Store(route, struct{}{})
return
}
switch rtspURL {
case "",
"rtsp://ip-addr/",
"rtsp://ip-addr",
"rtsp://ip-addr:pass@10.0.0.5:6667/blinkhd":
return
default:
route := strings.TrimSpace(strings.TrimPrefix(rtspURL, "rtsp://ip-addr/"))
rtspURLsFound.Store(route, struct{}{})
}
})
}
func saveRoutes(rtspURLs []string) {
contents := strings.Join(rtspURLs, "\n")
disgo.StartStep("Writing new dictionary file")
err := ioutil.WriteFile("dictionaries/routes", []byte(contents), 0644)
if err != nil {
disgo.FailStepf("unable to write dictionnary: %v", err)
}
}
+10
View File
@@ -0,0 +1,10 @@
module github.com/Ullaakut/cameradar/magefile
go 1.16
require (
github.com/Ullaakut/disgo v0.3.1
github.com/fatih/color v1.10.0 // indirect
github.com/magefile/mage v1.11.0
github.com/stretchr/testify v1.7.0 // indirect
)
+23
View File
@@ -0,0 +1,23 @@
github.com/Ullaakut/disgo v0.3.1 h1:BGGVHynji41KGuGI02ztTCnILRvyzlvmiCRl5bBpjKk=
github.com/Ullaakut/disgo v0.3.1/go.mod h1:/CSvpnYVSKOeh2dvUvx9cXshzz2t7T1/lRO/MrFj3fI=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls=
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+109
View File
@@ -0,0 +1,109 @@
//+build mage
package main
import (
"os"
"github.com/magefile/mage/sh"
"github.com/Ullaakut/disgo"
"github.com/Ullaakut/disgo/style"
)
var supportedPlatforms = map[string]string{
"linux/amd64": "ullaakut/cameradar:amd64",
"linux/386": "ullaakut/cameradar:386",
"linux/arm64": "ullaakut/cameradar:arm64",
"linux/arm/v7": "ullaakut/cameradar:armv7",
//"linux/riscv64": "ullaakut/cameradar:riscv64", // UNSUPPORTED.
//"linux/ppc64le": "ullaakut/cameradar:ppc64le", // UNSUPPORTED.
//"linux/s390x": "ullaakut/cameradar:s390x", // UNSUPPORTED.
//"linux/arm/v6": "ullaakut/cameradar:armv6", // UNSUPPORTED.
}
var Default = Build
// Follows https://www.docker.com/blog/multi-platform-docker-builds/.
func Build() error {
term := disgo.NewTerminal(disgo.WithColors(true))
term.StartStep("Building images for all platforms")
term.Infof("Builds planned for %v\n", supportedPlatforms)
for platform, name := range supportedPlatforms {
term.Infoln("Building image for", platform, "at", name)
// docker buildx build --platform linux/arm/v7 -t ullaakut/cameradar:armv7 .
if err := sh.Run("docker", "buildx", "build", "--platform", platform, "-t", name, "../../"); err != nil {
return term.FailStepf("unable to build image: %v", err)
}
}
term.Infoln(style.Success("Cross-platform docker build successful."))
return nil
}
func Publish() error {
term := disgo.NewTerminal(disgo.WithColors(true))
term.StartStep("Pushing images to DockerHub")
term.Infoln("Pushing ullaakut/cameradar:latest")
if err := sh.Run("docker", "push", "ullaakut/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
term.Infoln("Pushing ullaakut/cameradar:"+version)
if err := sh.Run("docker", "push", "ullaakut/cameradar:"+version); err != nil {
return term.FailStepf("unable to push versionned docker images to docker hub: %v", err)
}
}
term.StartStep("Pushing images to GitHub Packages")
term.Infoln("Pushing docker.pkg.github.com/ullaakut/cameradar/cameradar:latest")
if err := sh.Run("docker", "tag", "ullaakut/cameradar:latest", "docker.pkg.github.com/ullaakut/cameradar/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if err := sh.Run("docker", "push", "docker.pkg.github.com/ullaakut/cameradar/cameradar:latest"); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
term.Infoln("Pushing docker.pkg.github.com/ullaakut/cameradar/cameradar:"+version)
if err := sh.Run("docker", "tag", "ullaakut/cameradar:"+version, "docker.pkg.github.com/ullaakut/cameradar/cameradar:"+version); err != nil {
return term.FailStepf("unable to push latest docker images to docker hub: %v", err)
}
if err := sh.Run("docker", "push", "ullaakut/cameradar:"+version); err != nil {
return term.FailStepf("unable to push versionned docker images to docker hub: %v", err)
}
}
term.StartStep("Creating manifest(s) for cross platform builds")
var manifestImages []string
for _, image := range supportedPlatforms {
manifestImages = append(manifestImages, image)
}
args := []string{"manifest", "create", "--amend", "ullaakut/cameradar:latest"}
args = append(args, manifestImages...)
// docker manifest create ullaakut/cameradar:latest ullaakut/cameradar:amd64 ullaakut/cameradar:armv7 [...]
if err := sh.Run("docker", args...); err != nil {
return term.FailStepf("unable to create manifest: %v", err)
}
if version, exists := os.LookupEnv("CAMERADAR_VERSION"); exists {
args = []string{"manifest", "create", "--amend", "ullaakut/cameradar:"+version}
args = append(args, manifestImages...)
if err := sh.Run("docker", args...); err != nil {
return term.FailStepf("unable to create manifest: %v", err)
}
}
term.EndStep()
term.Infoln(style.Success("Images published successfully."))
return nil
}
-123
View File
@@ -1,123 +0,0 @@
## Golang
### Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
### Test binary, built with `go test -c`
*.test
### Output of the go coverage tool, specifically when used with LiteIDE
*.out
## MacOS
### General
.DS_Store
.AppleDouble
.LSOverride
### Icon must end with two \r
Icon
### Thumbnails
._*
### Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
### Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
## IDEs
### VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### JetBrains
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
-159
View File
@@ -1,159 +0,0 @@
# This file contains all available configuration options
# with their default values.
# options for analysis running
run:
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 1m
tests: false
# which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
- pkg/osfamilies
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# all available settings of specific linters
linters-settings:
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
# [deprecated] comma-separated list of pairs of the form pkg:regex
# the regex is used to ignore names within pkg. (default "fmt:.*").
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
ignore: fmt:.*,io/ioutil:^Read.*,os/exec:^Kill.*
govet:
# report about shadowed variables
check-shadowing: true
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/org/project
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 150
goconst:
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 3
depguard:
list-type: blacklist
include-go-root: false
packages:
- github.com/davecgh/go-spew/spew
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 120
# tab width in spaces. Default to 1.
tab-width: 1
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# call graph construction algorithm (cha, rta). In general, use cha for libraries,
# and rta for programs with main packages. Default is cha.
algo: cha
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
# True by default.
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
gocritic:
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
enabled-tags:
- performance
linters:
enable:
- megacheck
- govet
enable-all: false
disable:
- maligned
- prealloc
disable-all: false
presets:
- bugs
- unused
fast: false
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
exclude:
- "Subprocess launching should be audited"
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0
# Show only new issues: if there are unstaged changes or untracked files,
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
# It's a super-useful option for integration of golangci-lint into existing
# large codebase. It's not practical to fix all existing issues at the moment
# of integration: much better don't allow issues in new code.
# Default is false.
new: false
-20
View File
@@ -1,20 +0,0 @@
dist: trusty
sudo: required
language: go
before_install:
- sudo apt-get install -y nmap
- go get github.com/mattn/goveralls
script:
# Run unit tests
- go test -v -covermode=count -coverprofile=coverage.out
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci -repotoken=$COVERALLS_TOKEN
# Ensure the examples compile
- for dir in examples/*/; do go build -o $dir/bin $dir/main.go; done
notifications:
email:
recipients:
- brendan.le-glaunec@epitech.eu
on_success: never
on_failure: always
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2019 Ullaakut
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-114
View File
@@ -1,114 +0,0 @@
# nmap
<p align="center">
<img width="350" src="img/logo.png"/>
<p>
<p align="center">
<a href="LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" />
</a>
<a href="https://godoc.org/github.com/Ullaakut/nmap">
<img src="https://godoc.org/github.com/Ullaakut/cameradar?status.svg" />
</a>
<a href="https://goreportcard.com/report/github.com/ullaakut/nmap">
<img src="https://goreportcard.com/badge/github.com/ullaakut/nmap">
</a>
<a href="https://travis-ci.org/Ullaakut/nmap">
<img src="https://travis-ci.org/Ullaakut/nmap.svg?branch=master">
</a>
<a href="https://coveralls.io/github/Ullaakut/nmap?branch=master">
<img src="https://coveralls.io/repos/github/Ullaakut/nmap/badge.svg?branch=master">
</a>
<p>
This library aims at providing idiomatic `nmap` bindings for go developers, in order to make it easier to write security audit tools using golang.
<!-- It allows not only to parse the XML output of nmap, but also to get the output of nmap as it is running, through a channel. This can be useful for computing a scan's progress, or simply displaying live information to your users. -->
## It's currently a work in progress
This paragraph won't be removed until the library is ready to be used and properly documented.
## Supported features
- [x] All of `nmap`'s options as `WithXXX` methods.
- [x] Cancellable contexts support.
- [x] [Idiomatic go filters](examples/service_detection/main.go#L19).
- [x] Helpful enums for most nmap commands. (time templates, os families, port states, etc.)
- [x] Complete documentation of each option, mostly insipred from nmap's documentation.
## TODO
- [ ] Examples of usage - Work in progress (4/7 examples so far)
- [ ] Complete unit tests - Work in progress (95% coverage so far)
- [ ] Asynchronous scan
## Example
```go
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/Ullaakut/nmap"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
// with a 5 minute timeout.
scanner, err := nmap.NewScanner(
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
nmap.WithPorts("80,443,843"),
nmap.WithContext(ctx),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}
result, err := scanner.Run()
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
}
// Use the results to print an example output
for _, host := range result.Hosts {
if len(host.Ports) == 0 || len(host.Addresses) == 0 {
continue
}
fmt.Printf("Host %q:\n", host.Addresses[0])
for _, port := range host.Ports {
fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
}
}
fmt.Printf("Nmap done: %d hosts up scanned in %3f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
}
```
The program above outputs:
```bash
Host "172.217.16.46":
Port 80/tcp open http
Port 443/tcp open https
Port 843/tcp filtered unknown
Host "31.13.81.36":
Port 80/tcp open http
Port 443/tcp open https
Port 843/tcp open unknown
Host "216.58.215.110":
Port 80/tcp open http
Port 443/tcp open https
Port 843/tcp filtered unknown
Nmap done: 3 hosts up scanned in 1.29 seconds
```
-18
View File
@@ -1,18 +0,0 @@
package nmap
import (
"errors"
)
var (
// ErrNmapNotInstalled means that upon trying to manually locate nmap in the user's path,
// it was not found. Either use the WithBinaryPath method to set it manually, or make sure that
// the nmap binary is present in the user's $PATH.
ErrNmapNotInstalled = errors.New("'nmap' binary was not found")
// ErrScanTimeout means that the provided context was done before the scanner finished its scan.
ErrScanTimeout = errors.New("nmap scan timed out")
// ErrNoTargetsSpecified means that no targets were specified.
ErrNoTargetsSpecified = errors.New("no targets specified")
)
-64
View File
@@ -1,64 +0,0 @@
package nmap
import (
"fmt"
"log"
)
// A scanner can be instanciated with options to set the arguments
// that are given to nmap.
func ExampleScanner_simple() {
s, err := NewScanner(
WithTargets("google.com", "facebook.com", "youtube.com"),
WithCustomDNSServers("8.8.8.8", "8.8.4.4"),
WithTimingTemplate(TimingFastest),
WithTCPScanFlags(FlagACK, FlagNULL, FlagRST),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}
scanResult, err := s.Run()
if err != nil {
log.Fatalf("nmap encountered an error: %v", err)
}
fmt.Printf(
"Scan successful: %d hosts up\n",
scanResult.Stats.Hosts.Up,
)
// Output: Scan successful: 3 hosts up
}
// A scanner can be given custom idiomatic filters for both hosts
// and ports.
func ExampleScanner_filters() {
s, err := NewScanner(
WithTargets("google.com", "facebook.com"),
WithPorts("843"),
WithFilterHost(func(h Host) bool {
// Filter out hosts with no open ports.
for idx := range h.Ports {
if h.Ports[idx].Status() == "open" {
return true
}
}
return false
}),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}
scanResult, err := s.Run()
if err != nil {
log.Fatalf("nmap encountered an error: %v", err)
}
fmt.Printf(
"Filtered out hosts %d / Original number of hosts: %d\n",
len(scanResult.Hosts),
scanResult.Stats.Hosts.Total,
)
// Output: Filtered out hosts 1 / Original number of hosts: 2
}
-1216
View File
File diff suppressed because it is too large Load Diff
-1781
View File
File diff suppressed because it is too large Load Diff
-644
View File
@@ -1,644 +0,0 @@
package osfamilies
// OSFamily describes an OS Family, usually from a constructor or
// a kernel.
type OSFamily string
// OSFamily definitions.
const (
TwoN OSFamily = "2N"
TwoWire OSFamily = "2Wire"
ThreeCom OSFamily = "3Com"
ThreeM OSFamily = "3M"
FourG OSFamily = "4G"
ATec OSFamily = "A-Tec"
ADIC OSFamily = "ADIC"
AKCP OSFamily = "AKCP"
AMX OSFamily = "AMX"
APC OSFamily = "APC"
ARCA OSFamily = "ARCA"
ATandT OSFamily = "AT&T"
AVM OSFamily = "AVM"
AVtech OSFamily = "AVtech"
AXIS OSFamily = "AXIS"
Aastra OSFamily = "Aastra"
AcBel OSFamily = "AcBel"
Aceex OSFamily = "Aceex"
Acer OSFamily = "Acer"
Acme OSFamily = "Acme"
Acorp OSFamily = "Acorp"
Actiontec OSFamily = "Actiontec"
Adaptec OSFamily = "Adaptec"
Adtran OSFamily = "Adtran"
Adva OSFamily = "Adva"
Advanced OSFamily = "Advanced"
Aerohive OSFamily = "Aerohive"
Aethra OSFamily = "Aethra"
Agfa OSFamily = "Agfa"
AirLive OSFamily = "AirLive"
AirMagnet OSFamily = "AirMagnet"
AirSpan OSFamily = "AirSpan"
Airaya OSFamily = "Airaya"
Airlink101 OSFamily = "Airlink101"
Airnet OSFamily = "Airnet"
Airvana OSFamily = "Airvana"
Alaxala OSFamily = "Alaxala"
Alcatel OSFamily = "Alcatel"
AlcatelLucent OSFamily = "Alcatel-Lucent"
Alice OSFamily = "Alice"
AllenBradley OSFamily = "Allen-Bradley"
Allied OSFamily = "Allied"
Allnet OSFamily = "Allnet"
Allworx OSFamily = "Allworx"
Alvarion OSFamily = "Alvarion"
Amazon OSFamily = "Amazon"
Ambit OSFamily = "Ambit"
Amiga OSFamily = "Amiga"
Anue OSFamily = "Anue"
Apple OSFamily = "Apple"
Arcor OSFamily = "Arcor"
Areca OSFamily = "Areca"
Argon OSFamily = "Argon"
Argosy OSFamily = "Argosy"
Arris OSFamily = "Arris"
Aruba OSFamily = "Aruba"
Asmax OSFamily = "Asmax"
Asus OSFamily = "Asus"
Atari OSFamily = "Atari"
Atcom OSFamily = "Atcom"
AudioCodes OSFamily = "AudioCodes"
AudioControl OSFamily = "AudioControl"
Avaya OSFamily = "Avaya"
Avocent OSFamily = "Avocent"
Axcient OSFamily = "Axcient"
AzBox OSFamily = "AzBox"
BECK OSFamily = "BECK"
BSD OSFamily = "BSD"
BSDI OSFamily = "BSDI"
BT OSFamily = "BT"
Barracuda OSFamily = "Barracuda"
Barrelfish OSFamily = "Barrelfish"
Basler OSFamily = "Basler"
Bay OSFamily = "Bay"
BeaconMedaes OSFamily = "BeaconMedaes"
Beat OSFamily = "Beat"
Belkin OSFamily = "Belkin"
Bell OSFamily = "Bell"
Billion OSFamily = "Billion"
BinTec OSFamily = "BinTec"
BlackBox OSFamily = "BlackBox"
Blackboard OSFamily = "Blackboard"
Blue OSFamily = "Blue"
BlueArc OSFamily = "BlueArc"
Bluebird OSFamily = "Bluebird"
Bomara OSFamily = "Bomara"
Bosch OSFamily = "Bosch"
Bose OSFamily = "Bose"
Boundless OSFamily = "Boundless"
Bowers OSFamily = "Bowers"
British OSFamily = "British"
BroadMax OSFamily = "BroadMax"
Brocade OSFamily = "Brocade"
Brother OSFamily = "Brother"
Buffalo OSFamily = "Buffalo"
Burny OSFamily = "Burny"
Bush OSFamily = "Bush"
CNav OSFamily = "C-Nav"
CAEN OSFamily = "CAEN"
CMI OSFamily = "CMI"
Cabletron OSFamily = "Cabletron"
Caldera OSFamily = "Caldera"
Calix OSFamily = "Calix"
Cameo OSFamily = "Cameo"
Canon OSFamily = "Canon"
Casio OSFamily = "Casio"
Cayman OSFamily = "Cayman"
Ceedtec OSFamily = "Ceedtec"
Check OSFamily = "Check"
Chip OSFamily = "Chip"
CipherLab OSFamily = "CipherLab"
Cisco OSFamily = "Cisco"
Citrix OSFamily = "Citrix"
CoRAID OSFamily = "CoRAID"
Cobalt OSFamily = "Cobalt"
Cognex OSFamily = "Cognex"
Comau OSFamily = "Comau"
Compal OSFamily = "Compal"
Compaq OSFamily = "Compaq"
Comtrend OSFamily = "Comtrend"
Conceptronic OSFamily = "Conceptronic"
Control4 OSFamily = "Control4"
Coyote OSFamily = "Coyote"
Cray OSFamily = "Cray"
Crestron OSFamily = "Crestron"
CyanogenMod OSFamily = "CyanogenMod"
Cyberoam OSFamily = "Cyberoam"
Cymphonix OSFamily = "Cymphonix"
DLink OSFamily = "D-Link"
DEC OSFamily = "DEC"
DMP OSFamily = "DMP"
DTE OSFamily = "DTE"
DVTel OSFamily = "DVTel"
DYMO OSFamily = "DYMO"
Data OSFamily = "Data"
Datalogic OSFamily = "Datalogic"
Daysequerra OSFamily = "Daysequerra"
Decru OSFamily = "Decru"
Dedicated OSFamily = "Dedicated"
Dell OSFamily = "Dell"
Denon OSFamily = "Denon"
Denver OSFamily = "Denver"
Develop OSFamily = "Develop"
Dick OSFamily = "Dick"
Digi OSFamily = "Digi"
Digital OSFamily = "Digital"
Digitus OSFamily = "Digitus"
Digium OSFamily = "Digium"
DirecTV OSFamily = "DirecTV"
Dish OSFamily = "Dish"
Dolby OSFamily = "Dolby"
DragonFly OSFamily = "DragonFly"
DragonWave OSFamily = "DragonWave"
DrayTek OSFamily = "DrayTek"
Draytek OSFamily = "Draytek"
Drayton OSFamily = "Drayton"
Dream OSFamily = "Dream"
Drobo OSFamily = "Drobo"
EMC OSFamily = "EMC"
ESI OSFamily = "ESI"
ETH OSFamily = "ETH"
EasyPath OSFamily = "EasyPath"
Eaton OSFamily = "Eaton"
Efficient OSFamily = "Efficient"
Eicon OSFamily = "Eicon"
Elfiq OSFamily = "Elfiq"
Elk OSFamily = "Elk"
Elsag OSFamily = "Elsag"
Ember OSFamily = "Ember"
Emerson OSFamily = "Emerson"
EnGenius OSFamily = "EnGenius"
Encore OSFamily = "Encore"
Endian OSFamily = "Endian"
Enerdis OSFamily = "Enerdis"
Engetron OSFamily = "Engetron"
Enistic OSFamily = "Enistic"
Enlogic OSFamily = "Enlogic"
Enterasys OSFamily = "Enterasys"
Epson OSFamily = "Epson"
Ericsson OSFamily = "Ericsson"
Espressif OSFamily = "Espressif"
Essentia OSFamily = "Essentia"
EtherWerX OSFamily = "EtherWerX"
Exabyte OSFamily = "Exabyte"
Excito OSFamily = "Excito"
Express OSFamily = "Express"
Exterity OSFamily = "Exterity"
Extreme OSFamily = "Extreme"
F5 OSFamily = "F5"
FORE OSFamily = "FORE"
Fatek OSFamily = "Fatek"
FireBrick OSFamily = "FireBrick"
Force10 OSFamily = "Force10"
Fortinet OSFamily = "Fortinet"
Foscam OSFamily = "Foscam"
Foundry OSFamily = "Foundry"
Free OSFamily = "Free"
FreeBSD OSFamily = "FreeBSD"
FreeNAS OSFamily = "FreeNAS"
Freecom OSFamily = "Freecom"
Fronius OSFamily = "Fronius"
Frontier OSFamily = "Frontier"
Fuji OSFamily = "Fuji"
Fujian OSFamily = "Fujian"
Fujitsu OSFamily = "Fujitsu"
Funkwerk OSFamily = "Funkwerk"
GNU OSFamily = "GNU"
GalaxyMetalGear OSFamily = "GalaxyMetalGear"
Gargoyle OSFamily = "Gargoyle"
Garmin OSFamily = "Garmin"
GbE2c OSFamily = "GbE2c"
Geist OSFamily = "Geist"
Gemtek OSFamily = "Gemtek"
General OSFamily = "General"
Generex OSFamily = "Generex"
Gennet OSFamily = "Gennet"
Genua OSFamily = "Genua"
George OSFamily = "George"
Geovision OSFamily = "Geovision"
GlobespanVirata OSFamily = "GlobespanVirata"
GoPro OSFamily = "GoPro"
Google OSFamily = "Google"
Grace OSFamily = "Grace"
Grandstream OSFamily = "Grandstream"
Green OSFamily = "Green"
H3C OSFamily = "H3C"
HID OSFamily = "HID"
HP OSFamily = "HP"
HW OSFamily = "HW"
Haiku OSFamily = "Haiku"
Hamlet OSFamily = "Hamlet"
Harris OSFamily = "Harris"
Hawking OSFamily = "Hawking"
Hay OSFamily = "Hay"
Head OSFamily = "Head"
Henry OSFamily = "Henry"
HighFlying OSFamily = "High-Flying"
Hikvision OSFamily = "Hikvision"
Hioki OSFamily = "Hioki"
Hirschmann OSFamily = "Hirschmann"
Hitron OSFamily = "Hitron"
Hotway OSFamily = "Hotway"
Huawei OSFamily = "Huawei"
Hybertone OSFamily = "Hybertone"
IBM OSFamily = "IBM"
IEI OSFamily = "IEI"
IGEL OSFamily = "IGEL"
IHome OSFamily = "IHome"
IOData OSFamily = "IO-Data"
IOGear OSFamily = "IOGear"
ION OSFamily = "ION"
IPAD OSFamily = "IPAD"
IPCop OSFamily = "IPCop"
IPFire OSFamily = "IPFire"
ISS OSFamily = "ISS"
ITW OSFamily = "ITW"
Icom OSFamily = "Icom"
Icy OSFamily = "Icy"
Imperva OSFamily = "Imperva"
Infoblox OSFamily = "Infoblox"
Infomir OSFamily = "Infomir"
Infrant OSFamily = "Infrant"
Inova OSFamily = "Inova"
Instar OSFamily = "Instar"
Intel OSFamily = "Intel"
Interbell OSFamily = "Interbell"
Interflex OSFamily = "Interflex"
Intermec OSFamily = "Intermec"
Interpeak OSFamily = "Interpeak"
Intertex OSFamily = "Intertex"
Intracom OSFamily = "Intracom"
Inventel OSFamily = "Inventel"
Iomega OSFamily = "Iomega"
IronPort OSFamily = "IronPort"
Isilon OSFamily = "Isilon"
Iskratel OSFamily = "Iskratel"
JTEKT OSFamily = "JTEKT"
Joyent OSFamily = "Joyent"
Juniper OSFamily = "Juniper"
KA9Q OSFamily = "KA9Q"
KCorp OSFamily = "KCorp"
KWSoftware OSFamily = "KW-Software"
KabaBenzing OSFamily = "Kaba-Benzing"
Kaiomy OSFamily = "Kaiomy"
Kapsch OSFamily = "Kapsch"
Kartina OSFamily = "Kartina"
Kemp OSFamily = "Kemp"
Keyence OSFamily = "Keyence"
Kodak OSFamily = "Kodak"
Kongsberg OSFamily = "Kongsberg"
Konica OSFamily = "Konica"
Koukaam OSFamily = "Koukaam"
Kronos OSFamily = "Kronos"
Kyocera OSFamily = "Kyocera"
LG OSFamily = "LG"
LaCie OSFamily = "LaCie"
LaCrosse OSFamily = "LaCrosse"
LaSAT OSFamily = "LaSAT"
Lancom OSFamily = "Lancom"
Lanier OSFamily = "Lanier"
Lantronix OSFamily = "Lantronix"
Larus OSFamily = "Larus"
Leica OSFamily = "Leica"
Lenel OSFamily = "Lenel"
Leolink OSFamily = "Leolink"
LevelOne OSFamily = "LevelOne"
Lexmark OSFamily = "Lexmark"
Liebert OSFamily = "Liebert"
LifeSize OSFamily = "LifeSize"
Linksys OSFamily = "Linksys"
Linux OSFamily = "Linux"
LogiLink OSFamily = "LogiLink"
Logitech OSFamily = "Logitech"
Lorex OSFamily = "Lorex"
Lucent OSFamily = "Lucent"
Luminary OSFamily = "Luminary"
Luxul OSFamily = "Luxul"
Lyngsoe OSFamily = "Lyngsoe"
MGE OSFamily = "MGE"
MOXA OSFamily = "MOXA"
MPI OSFamily = "MPI"
Macsense OSFamily = "Macsense"
Maipu OSFamily = "Maipu"
Mapower OSFamily = "Mapower"
Marantz OSFamily = "Marantz"
McAfee OSFamily = "McAfee"
Meinberg OSFamily = "Meinberg"
Meru OSFamily = "Meru"
Metrix OSFamily = "Metrix"
MicroNet OSFamily = "MicroNet"
Microsoft OSFamily = "Microsoft"
Microware OSFamily = "Microware"
MikroTik OSFamily = "MikroTik"
Milight OSFamily = "Milight"
Minix OSFamily = "Minix"
Minolta OSFamily = "Minolta"
Mirapoint OSFamily = "Mirapoint"
Mitel OSFamily = "Mitel"
Mitrastar OSFamily = "Mitrastar"
Mitsubishi OSFamily = "Mitsubishi"
Modtronix OSFamily = "Modtronix"
Motorola OSFamily = "Motorola"
MusicianLink OSFamily = "MusicianLink"
NCR OSFamily = "NCR"
NEC OSFamily = "NEC"
NOXON OSFamily = "NOXON"
NRG OSFamily = "NRG"
NSFOCUS OSFamily = "NSFOCUS"
NTI OSFamily = "NTI"
NTT OSFamily = "NTT"
Nashuatec OSFamily = "Nashuatec"
National OSFamily = "National"
NeXT OSFamily = "NeXT"
Neopost OSFamily = "Neopost"
Ness OSFamily = "Ness"
Nest OSFamily = "Nest"
NetApp OSFamily = "NetApp"
NetBSD OSFamily = "NetBSD"
NetBurner OSFamily = "NetBurner"
NetOptics OSFamily = "NetOptics"
Netasq OSFamily = "Netasq"
Netcomm OSFamily = "Netcomm"
Netgear OSFamily = "Netgear"
Netgem OSFamily = "Netgem"
Netopia OSFamily = "Netopia"
Network OSFamily = "Network"
NetworkAlchemy OSFamily = "NetworkAlchemy"
NetworksAOK OSFamily = "NetworksAOK"
Neuf OSFamily = "Neuf"
Newave OSFamily = "Newave"
NexStor OSFamily = "NexStor"
Nexenta OSFamily = "Nexenta"
Nexsan OSFamily = "Nexsan"
Nibe OSFamily = "Nibe"
Nintendo OSFamily = "Nintendo"
NodeMCU OSFamily = "NodeMCU"
Nokia OSFamily = "Nokia"
Nomadix OSFamily = "Nomadix"
Nortel OSFamily = "Nortel"
Novatel OSFamily = "Novatel"
Novell OSFamily = "Novell"
NutOS OSFamily = "Nut/OS"
OSRAM OSFamily = "OSRAM"
Obihai OSFamily = "Obihai"
Ocean OSFamily = "Ocean"
Oki OSFamily = "Oki"
Olivetti OSFamily = "Olivetti"
Olympus OSFamily = "Olympus"
Omron OSFamily = "Omron"
On OSFamily = "On"
OnStor OSFamily = "OnStor"
Onboard OSFamily = "Onboard"
OneAccess OSFamily = "OneAccess"
OpenBSD OSFamily = "OpenBSD"
OpenBox OSFamily = "OpenBox"
Opto OSFamily = "Opto"
Oracle OSFamily = "Oracle"
Orange OSFamily = "Orange"
Osmosys OSFamily = "Osmosys"
Ouya OSFamily = "Ouya"
PCBSD OSFamily = "PC-BSD"
PCMeasure OSFamily = "PCMeasure"
PORTech OSFamily = "PORTech"
Packard OSFamily = "Packard"
Packet8 OSFamily = "Packet8"
PacketFront OSFamily = "PacketFront"
Packeteer OSFamily = "Packeteer"
Palmmicro OSFamily = "Palmmicro"
Palo OSFamily = "Palo"
Panasas OSFamily = "Panasas"
Panasonic OSFamily = "Panasonic"
Papouch OSFamily = "Papouch"
Patton OSFamily = "Patton"
Peplink OSFamily = "Peplink"
Perfectone OSFamily = "Perfectone"
Perle OSFamily = "Perle"
Phar OSFamily = "Phar"
PheeNet OSFamily = "PheeNet"
Philips OSFamily = "Philips"
Phoenix OSFamily = "Phoenix"
Pingtel OSFamily = "Pingtel"
Pioneer OSFamily = "Pioneer"
Pirelli OSFamily = "Pirelli"
Planet OSFamily = "Planet"
Polycom OSFamily = "Polycom"
Precise OSFamily = "Precise"
Printronix OSFamily = "Printronix"
Priva OSFamily = "Priva"
Promise OSFamily = "Promise"
Proxim OSFamily = "Proxim"
QEMU OSFamily = "QEMU"
QNAP OSFamily = "QNAP"
QNX OSFamily = "QNX"
QTech OSFamily = "QTech"
Qualisys OSFamily = "Qualisys"
Quantum OSFamily = "Quantum"
Quarterdeck OSFamily = "Quarterdeck"
RAD OSFamily = "RAD"
RCA OSFamily = "RCA"
RF OSFamily = "RF"
RFSpace OSFamily = "RF-Space"
RGB OSFamily = "RGB"
RIM OSFamily = "RIM"
RISCOS OSFamily = "RISCOS"
RISE OSFamily = "RISE"
RSA OSFamily = "RSA"
Rabbit OSFamily = "Rabbit"
Radware OSFamily = "Radware"
Raritan OSFamily = "Raritan"
ReactOS OSFamily = "ReactOS"
RedM OSFamily = "Red-M"
Redback OSFamily = "Redback"
Reliable OSFamily = "Reliable"
Repotech OSFamily = "Repotech"
Revo OSFamily = "Revo"
Ricoh OSFamily = "Ricoh"
Rigol OSFamily = "Rigol"
Rio OSFamily = "Rio"
Riverbed OSFamily = "Riverbed"
Roberts OSFamily = "Roberts"
Rockwell OSFamily = "Rockwell"
Roku OSFamily = "Roku"
Ruckus OSFamily = "Ruckus"
RuggedCom OSFamily = "RuggedCom"
Ruijie OSFamily = "Ruijie"
SCO OSFamily = "SCO"
SEH OSFamily = "SEH"
SGI OSFamily = "SGI"
SMA OSFamily = "SMA"
SMC OSFamily = "SMC"
SNR OSFamily = "SNR"
Sagem OSFamily = "Sagem"
Sagemcom OSFamily = "Sagemcom"
Samsung OSFamily = "Samsung"
Sandstrom OSFamily = "Sandstrom"
Sanyo OSFamily = "Sanyo"
Sapling OSFamily = "Sapling"
Satel OSFamily = "Satel"
Savin OSFamily = "Savin"
Schneider OSFamily = "Schneider"
Schrack OSFamily = "Schrack"
Schweitzer OSFamily = "Schweitzer"
Scientific OSFamily = "Scientific"
Seagate OSFamily = "Seagate"
Secure OSFamily = "Secure"
Seiko OSFamily = "Seiko"
Senao OSFamily = "Senao"
Sensatronics OSFamily = "Sensatronics"
Sequent OSFamily = "Sequent"
Sharp OSFamily = "Sharp"
Shenzhen OSFamily = "Shenzhen"
ShoreTel OSFamily = "ShoreTel"
Siemens OSFamily = "Siemens"
Silicondust OSFamily = "Silicondust"
Sinus OSFamily = "Sinus"
Sipura OSFamily = "Sipura"
Sitecom OSFamily = "Sitecom"
Sling OSFamily = "Sling"
Slingbox OSFamily = "Slingbox"
Smart OSFamily = "Smart"
Smartlink OSFamily = "Smartlink"
Snom OSFamily = "Snom"
Solwise OSFamily = "Solwise"
SonicWALL OSFamily = "SonicWALL"
Sonos OSFamily = "Sonos"
Sonus OSFamily = "Sonus"
Sony OSFamily = "Sony"
Source OSFamily = "Source"
Specialix OSFamily = "Specialix"
Sphairon OSFamily = "Sphairon"
Star OSFamily = "Star"
Starbridge OSFamily = "Starbridge"
Stonewater OSFamily = "Stonewater"
StorageTek OSFamily = "StorageTek"
Stratus OSFamily = "Stratus"
Suga OSFamily = "Suga"
Sun OSFamily = "Sun"
SunPower OSFamily = "SunPower"
Supermicro OSFamily = "Supermicro"
Syllable OSFamily = "Syllable"
Symantec OSFamily = "Symantec"
Symbian OSFamily = "Symbian"
Symbol OSFamily = "Symbol"
Symmetricon OSFamily = "Symmetricon"
Synology OSFamily = "Synology"
THome OSFamily = "T-Home"
TMarc OSFamily = "T-Marc"
TPLINK OSFamily = "TP-LINK"
TPLink OSFamily = "TP-Link"
TRENDnet OSFamily = "TRENDnet"
Tadiran OSFamily = "Tadiran"
Tahoe OSFamily = "Tahoe"
Tandberg OSFamily = "Tandberg"
Tandem OSFamily = "Tandem"
TechniSat OSFamily = "TechniSat"
Tektronix OSFamily = "Tektronix"
Telco OSFamily = "Telco"
Teldat OSFamily = "Teldat"
Telekom OSFamily = "Telekom"
Telewell OSFamily = "Telewell"
Telex OSFamily = "Telex"
Telsey OSFamily = "Telsey"
Teltronics OSFamily = "Teltronics"
TenAsys OSFamily = "TenAsys"
Tenda OSFamily = "Tenda"
Teradici OSFamily = "Teradici"
Terratec OSFamily = "Terratec"
Texas OSFamily = "Texas"
Thales OSFamily = "Thales"
Thecus OSFamily = "Thecus"
Thomson OSFamily = "Thomson"
Tiandy OSFamily = "Tiandy"
Tibbo OSFamily = "Tibbo"
Tigo OSFamily = "Tigo"
Tintri OSFamily = "Tintri"
TippingPoint OSFamily = "TippingPoint"
Tizen OSFamily = "Tizen"
Topfield OSFamily = "Topfield"
Toptech OSFamily = "Toptech"
Toshiba OSFamily = "Toshiba"
Trane OSFamily = "Trane"
TransAct OSFamily = "TransAct"
Tranzeo OSFamily = "Tranzeo"
Trapeze OSFamily = "Trapeze"
Tripp OSFamily = "Tripp"
Tut OSFamily = "Tut"
Tyco OSFamily = "Tyco"
USRobotics OSFamily = "USRobotics"
UTStarcom OSFamily = "UTStarcom"
Ubee OSFamily = "Ubee"
Ubicom OSFamily = "Ubicom"
Ubiquiti OSFamily = "Ubiquiti"
Universal OSFamily = "Universal"
VBrick OSFamily = "VBrick"
VIPA OSFamily = "VIPA"
VMware OSFamily = "VMware"
VTrak OSFamily = "VTrak"
Vantage OSFamily = "Vantage"
Vegastream OSFamily = "Vegastream"
Viasat OSFamily = "Viasat"
Vilar OSFamily = "Vilar"
Virdi OSFamily = "Virdi"
Visual OSFamily = "Visual"
Vocality OSFamily = "Vocality"
Vodafone OSFamily = "Vodafone"
Vodavi OSFamily = "Vodavi"
Vonage OSFamily = "Vonage"
WandT OSFamily = "W&T"
WAGO OSFamily = "WAGO"
WIZnet OSFamily = "WIZnet"
Wago OSFamily = "Wago"
Walker OSFamily = "Walker"
WatchGuard OSFamily = "WatchGuard"
WebSense OSFamily = "WebSense"
Welltech OSFamily = "Welltech"
Westell OSFamily = "Westell"
Westermo OSFamily = "Westermo"
Western OSFamily = "Western"
Wind OSFamily = "Wind"
Windows OSFamily = "Windows"
World OSFamily = "World"
WowWee OSFamily = "WowWee"
Wyse OSFamily = "Wyse"
XAVi OSFamily = "XAVi"
XEUdotCom OSFamily = "XEU.com"
XMOS OSFamily = "XMOS"
Xerox OSFamily = "Xerox"
Xiaomi OSFamily = "Xiaomi"
Xirrus OSFamily = "Xirrus"
Xylan OSFamily = "Xylan"
Xyplex OSFamily = "Xyplex"
Yamaha OSFamily = "Yamaha"
Yealink OSFamily = "Yealink"
ZKTeco OSFamily = "ZKTeco"
ZTE OSFamily = "ZTE"
Zebra OSFamily = "Zebra"
Zelax OSFamily = "Zelax"
Zerto OSFamily = "Zerto"
Zhone OSFamily = "Zhone"
Zipato OSFamily = "Zipato"
ZoneAlarm OSFamily = "ZoneAlarm"
Zoom OSFamily = "Zoom"
ZyXEL OSFamily = "ZyXEL"
Zyfer OSFamily = "Zyfer"
cab OSFamily = "cab"
eCosCentric OSFamily = "eCosCentric"
iDirect OSFamily = "iDirect"
iPXE OSFamily = "iPXE"
iRobot OSFamily = "iRobot"
illumos OSFamily = "illumos"
ipTIME OSFamily = "ipTIME"
lwIP OSFamily = "lwIP"
m3 OSFamily = "m3"
mbNet OSFamily = "mbNet"
nCircle OSFamily = "nCircle"
)
-508
View File
@@ -1,508 +0,0 @@
package nmap
import (
"encoding/xml"
"io/ioutil"
"strconv"
"time"
family "github.com/Ullaakut/nmap/pkg/osfamilies"
)
// Run represents an nmap scanning run.
type Run struct {
XMLName xml.Name `xml:"nmaprun"`
Args string `xml:"args,attr" json:"args"`
ProfileName string `xml:"profile_name,attr" json:"profile_name"`
Scanner string `xml:"scanner,attr" json:"scanner"`
StartStr string `xml:"startstr,attr" json:"start_str"`
Version string `xml:"version,attr" json:"version"`
XMLOutputVersion string `xml:"xmloutputversion,attr" json:"xml_output_version"`
Debugging Debugging `xml:"debugging" json:"debugging"`
Stats Stats `xml:"runstats" json:"run_stats"`
ScanInfo ScanInfo `xml:"scaninfo" json:"scan_info"`
Start Timestamp `xml:"start,attr" json:"start"`
Verbose Verbose `xml:"verbose" json:"verbose"`
Hosts []Host `xml:"host" json:"hosts"`
PostScripts []Script `xml:"postscript>script" json:"post_scripts"`
PreScripts []Script `xml:"prescript>script" json:"pre_scripts"`
Targets []Target `xml:"target" json:"targets"`
TaskBegin []Task `xml:"taskbegin" json:"task_begin"`
TaskProgress []TaskProgress `xml:"taskprogress" json:"task_progress"`
TaskEnd []Task `xml:"taskend" json:"task_end"`
rawXML []byte
}
// ToFile writes a Run as XML into the specified file path.
func (r Run) ToFile(filePath string) error {
return ioutil.WriteFile(filePath, r.rawXML, 0666)
}
// ScanInfo represents the scan information.
type ScanInfo struct {
NumServices int `xml:"numservices,attr" json:"num_services"`
Protocol string `xml:"protocol,attr" json:"protocol"`
ScanFlags string `xml:"scanflags,attr" json:"scan_flags"`
Services string `xml:"services,attr" json:"services"`
Type string `xml:"type,attr" json:"type"`
}
// Verbose contains the verbosity level of the scan.
type Verbose struct {
Level int `xml:"level,attr" json:"level"`
}
// Debugging contains the debugging level of the scan.
type Debugging struct {
Level int `xml:"level,attr" json:"level"`
}
// Task contains information about a task.
type Task struct {
Time Timestamp `xml:"time,attr" json:"time"`
Task string `xml:"task,attr" json:"task"`
ExtraInfo string `xml:"extrainfo,attr" json:"extra_info"`
}
// TaskProgress contains information about the progression of a task.
type TaskProgress struct {
Percent float32 `xml:"percent,attr" json:"percent"`
Remaining int `xml:"remaining,attr" json:"remaining"`
Task string `xml:"task,attr" json:"task"`
Etc Timestamp `xml:"etc,attr" json:"etc"`
Time Timestamp `xml:"time,attr" json:"time"`
}
// Target represents a target, how it was specified when passed to nmap,
// its status and the reason for its status. Example:
// <target specification="domain.does.not.exist" status="skipped" reason="invalid"/>
type Target struct {
Specification string `xml:"specification,attr" json:"specification"`
Status string `xml:"status,attr" json:"status"`
Reason string `xml:"reason,attr" json:"reason"`
}
// Host represents a host that was scanned.
type Host struct {
Distance Distance `xml:"distance" json:"distance"`
EndTime Timestamp `xml:"endtime,attr,omitempty" json:"end_time"`
IPIDSequence IPIDSequence `xml:"ipidsequence" json:"ip_id_sequence"`
OS OS `xml:"os" json:"os"`
StartTime Timestamp `xml:"starttime,attr,omitempty" json:"start_time"`
Status Status `xml:"status" json:"status"`
TCPSequence TCPSequence `xml:"tcpsequence" json:"tcp_sequence"`
TCPTSSequence TCPTSSequence `xml:"tcptssequence" json:"tcp_ts_sequence"`
Times Times `xml:"times" json:"times"`
Trace Trace `xml:"trace" json:"trace"`
Uptime Uptime `xml:"uptime" json:"uptime"`
Comment string `xml:"comment,attr" json:"comment"`
Addresses []Address `xml:"address" json:"addresses"`
ExtraPorts []ExtraPort `xml:"ports>extraports" json:"extra_ports"`
Hostnames []Hostname `xml:"hostnames>hostname" json:"hostnames"`
HostScripts []Script `xml:"hostscript>script" json:"host_scripts"`
Ports []Port `xml:"ports>port" json:"ports"`
Smurfs []Smurf `xml:"smurf" json:"smurfs"`
}
// Status represents a host's status.
type Status struct {
State string `xml:"state,attr" json:"state"`
Reason string `xml:"reason,attr" json:"reason"`
ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
}
func (s Status) String() string {
return s.State
}
// Address contains a IPv4 or IPv6 address for a host.
type Address struct {
Addr string `xml:"addr,attr" json:"addr"`
AddrType string `xml:"addrtype,attr" json:"addr_type"`
Vendor string `xml:"vendor,attr" json:"vendor"`
}
func (a Address) String() string {
return a.Addr
}
// Hostname is a name for a host.
type Hostname struct {
Name string `xml:"name,attr" json:"name"`
Type string `xml:"type,attr" json:"type"`
}
func (h Hostname) String() string {
return h.Name
}
// Smurf contains repsonses from a smurf attack.
type Smurf struct {
Responses string `xml:"responses,attr" json:"responses"`
}
// ExtraPort contains the information about the closed and filtered ports.
type ExtraPort struct {
State string `xml:"state,attr" json:"state"`
Count int `xml:"count,attr" json:"count"`
Reasons []Reason `xml:"extrareasons" json:"reasons"`
}
// Reason represents a reason why a port is closed or filtered.
// This won't be in the scan results unless WithReason is used.
type Reason struct {
Reason string `xml:"reason,attr" json:"reason"`
Count int `xml:"count,attr" json:"count"`
}
// Port contains all the information about a scanned port.
type Port struct {
ID uint16 `xml:"portid,attr" json:"id"`
Protocol string `xml:"protocol,attr" json:"protocol"`
Owner Owner `xml:"owner" json:"owner"`
Service Service `xml:"service" json:"service"`
State State `xml:"state" json:"state"`
Scripts []Script `xml:"script" json:"scripts"`
}
// PortStatus represents a port's state.
type PortStatus string
// Enumerates the different possible state values.
const (
Open PortStatus = "open"
Closed PortStatus = "closed"
Filtered PortStatus = "filtered"
Unfiltered PortStatus = "unfiltered"
)
// Status returns the status of a port.
func (p Port) Status() PortStatus {
return PortStatus(p.State.State)
}
// State contains information about a given port's status.
// State will be open, closed, etc.
type State struct {
State string `xml:"state,attr" json:"state"`
Reason string `xml:"reason,attr" json:"reason"`
ReasonIP string `xml:"reason_ip,attr" json:"reason_ip"`
ReasonTTL float32 `xml:"reason_ttl,attr" json:"reason_ttl"`
}
func (s State) String() string {
return s.State
}
// Owner contains the name of a port's owner.
type Owner struct {
Name string `xml:"name,attr" json:"name"`
}
func (o Owner) String() string {
return o.Name
}
// Service contains detailed information about a service on an open port.
type Service struct {
DeviceType string `xml:"devicetype,attr" json:"device_type"`
ExtraInfo string `xml:"extrainfo,attr" json:"extra_info"`
HighVersion string `xml:"highver,attr" json:"high_version"`
Hostname string `xml:"hostname,attr" json:"hostname"`
LowVersion string `xml:"lowver,attr" json:"low_version"`
Method string `xml:"method,attr" json:"method"`
Name string `xml:"name,attr" json:"name"`
OSType string `xml:"ostype,attr" json:"os_type"`
Product string `xml:"product,attr" json:"product"`
Proto string `xml:"proto,attr" json:"proto"`
RPCNum string `xml:"rpcnum,attr" json:"rpc_num"`
ServiceFP string `xml:"servicefp,attr" json:"service_fp"`
Tunnel string `xml:"tunnel,attr" json:"tunnel"`
Version string `xml:"version,attr" json:"version"`
Configuration int `xml:"conf,attr" json:"configuration"`
CPEs []CPE `xml:"cpe" json:"cpes"`
}
func (s Service) String() string {
return s.Name
}
// CPE (Common Platform Enumeration) is a standardized way to name software
// applications, operating systems and hardware platforms.
type CPE string
// Script represents an Nmap Scripting Engine script.
type Script struct {
ID string `xml:"id,attr" json:"id"`
Output string `xml:"output,attr" json:"output"`
Tables Table `xml:"table" json:"tables"`
}
// Table contains the output of the script in an easily parsable form.
type Table map[string]string
// MarshalXML implements the xml.Marshaler interface.
func (t Table) MarshalXML(e *xml.Encoder, startElem xml.StartElement) error {
tokens := []xml.Token{startElem}
// Add all key/value pairs as entries in the XML array.
for key, value := range t {
// Start of the XML element.
start := xml.StartElement{
Name: xml.Name{
Local: "elem",
},
Attr: []xml.Attr{
{
Name: xml.Name{
Local: "key",
},
Value: key,
},
},
}
// End of the XML element.
end := xml.EndElement{
Name: start.Name,
}
// Append the start, content and end of the new element to the list of XML tokens.
tokens = append(tokens, start, xml.CharData(value), end)
}
tokens = append(tokens, xml.EndElement{
Name: startElem.Name,
})
// Encode all tokens.
for _, t := range tokens {
err := e.EncodeToken(t)
if err != nil {
return err
}
}
// Flush the encoder to ensure that the tokens are written.
err := e.Flush()
if err != nil {
return err
}
return nil
}
// UnmarshalXML implements the xml.Unmarshaler interface.
func (t *Table) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
table := make(map[string]string)
var (
currentKey string
currentValue string
)
for {
token, err := d.Token()
if err != nil {
break
}
switch element := token.(type) {
case xml.StartElement:
for _, attribute := range element.Attr {
if attribute.Name.Local == "key" {
currentKey = attribute.Value
break
}
}
case xml.CharData:
currentValue = string(element)
case xml.EndElement:
// Insert the current key/value pair.
table[currentKey] = currentValue
// Reset the temporary variables for the next pair.
currentKey = ""
currentValue = ""
}
}
*t = table
return nil
}
// OS contains the fingerprinted operating system for a host.
type OS struct {
PortsUsed []PortUsed `xml:"portused" json:"ports_used"`
Matches []OSMatch `xml:"osmatch" json:"os_matches"`
Fingerprints []OSFingerprint `xml:"osfingerprint" json:"os_fingerprints"`
Classes []OSClass `xml:"osclass" json:"os_classes"`
}
// PortUsed is the port used to fingerprint an operating system.
type PortUsed struct {
State string `xml:"state,attr" json:"state"`
Proto string `xml:"proto,attr" json:"proto"`
ID int `xml:"portid,attr" json:"port_id"`
}
// OSMatch contains detailed information regarding an operating system fingerprint.
type OSMatch struct {
Name string `xml:"name,attr" json:"name"`
Accuracy int `xml:"accuracy,attr" json:"accuracy"`
Line int `xml:"line,attr" json:"line"`
}
// OSClass contains vendor information about an operating system.
type OSClass struct {
Vendor string `xml:"vendor,attr" json:"vendor"`
OSGeneration string `xml:"osgen,attr" json:"os_generation"`
Type string `xml:"type,attr" json:"type"`
Accuracy int `xml:"accuracy,attr" json:"accuracy"`
Family string `xml:"osfamily,attr" json:"os_family"`
CPEs []CPE `xml:"cpe" json:"cpes"`
}
// OSFamily returns the OS family in an enumerated format.
func (o OSClass) OSFamily() family.OSFamily {
return family.OSFamily(o.Family)
}
// OSFingerprint is the actual fingerprint string of an operating system.
type OSFingerprint struct {
Fingerprint string `xml:"fingerprint,attr" json:"fingerprint"`
}
// Distance is the amount of hops to a particular host.
type Distance struct {
Value int `xml:"value,attr" json:"value"`
}
// Uptime is the amount of time the host has been up.
type Uptime struct {
Seconds int `xml:"seconds,attr" json:"seconds"`
Lastboot string `xml:"lastboot,attr" json:"last_boot"`
}
// Sequence represents a detected sequence.
type Sequence struct {
Class string `xml:"class,attr" json:"class"`
Values string `xml:"values,attr" json:"values"`
}
// TCPSequence represents a detected TCP sequence.
type TCPSequence struct {
Index int `xml:"index,attr" json:"index"`
Difficulty string `xml:"difficulty,attr" json:"difficulty"`
Values string `xml:"values,attr" json:"values"`
}
// IPIDSequence represents a detected IP ID sequence.
type IPIDSequence Sequence
// TCPTSSequence represents a detected TCP TS sequence.
type TCPTSSequence Sequence
// Trace represents the trace to a host, including the hops.
type Trace struct {
Proto string `xml:"proto,attr" json:"proto"`
Port int `xml:"port,attr" json:"port"`
Hops []Hop `xml:"hop" json:"hops"`
}
// Hop is an IP hop to a host.
type Hop struct {
TTL float32 `xml:"ttl,attr" json:"ttl"`
RTT string `xml:"rtt,attr" json:"rtt"`
IPAddr string `xml:"ipaddr,attr" json:"ip_addr"`
Host string `xml:"host,attr" json:"host"`
}
// Times contains time statistics for an nmap scan.
type Times struct {
SRTT string `xml:"srtt,attr" json:"srtt"`
RTT string `xml:"rttvar,attr" json:"rttv"`
To string `xml:"to,attr" json:"to"`
}
// Stats contains statistics for an nmap scan.
type Stats struct {
Finished Finished `xml:"finished" json:"finished"`
Hosts HostStats `xml:"hosts" json:"hosts"`
}
// Finished contains detailed statistics regarding a finished scan.
type Finished struct {
Time Timestamp `xml:"time,attr" json:"time"`
TimeStr string `xml:"timestr,attr" json:"time_str"`
Elapsed float32 `xml:"elapsed,attr" json:"elapsed"`
Summary string `xml:"summary,attr" json:"summary"`
Exit string `xml:"exit,attr" json:"exit"`
ErrorMsg string `xml:"errormsg,attr" json:"error_msg"`
}
// HostStats contains the amount of up and down hosts and the total count.
type HostStats struct {
Up int `xml:"up,attr" json:"up"`
Down int `xml:"down,attr" json:"down"`
Total int `xml:"total,attr" json:"total"`
}
// Timestamp represents time as a UNIX timestamp in seconds.
type Timestamp time.Time
// ParseTime converts a UNIX timestamp string to a time.Time.
func (t *Timestamp) ParseTime(s string) error {
timestamp, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return err
}
*t = Timestamp(time.Unix(timestamp, 0))
return nil
}
// FormatTime formats the time.Time value as a UNIX timestamp string.
func (t Timestamp) FormatTime() string {
return strconv.FormatInt(time.Time(t).Unix(), 10)
}
// MarshalJSON implements the json.Marshaler interface.
func (t Timestamp) MarshalJSON() ([]byte, error) {
return []byte(t.FormatTime()), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *Timestamp) UnmarshalJSON(b []byte) error {
return t.ParseTime(string(b))
}
// MarshalXMLAttr implements the xml.MarshalerAttr interface.
func (t Timestamp) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
if time.Time(t).IsZero() {
return xml.Attr{}, nil
}
return xml.Attr{Name: name, Value: t.FormatTime()}, nil
}
// UnmarshalXMLAttr implements the xml.UnmarshalXMLAttr interface.
func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
return t.ParseTime(attr.Value)
}
// Parse takes a byte array of nmap xml data and unmarshals it into a
// Run struct.
func Parse(content []byte) (*Run, error) {
r := &Run{
rawXML: content,
}
err := xml.Unmarshal(content, r)
return r, err
}
-939
View File
@@ -1,939 +0,0 @@
package nmap
import (
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"reflect"
"testing"
"time"
family "github.com/Ullaakut/nmap/pkg/osfamilies"
)
func TestParseTime(t *testing.T) {
ts := Timestamp{}
err := ts.ParseTime("invalid")
if err == nil {
t.Errorf("expected strconv.ParseInt: parsing \"invalid\": invalid syntax got %v", err)
}
}
func TestFormatTime(t *testing.T) {
originalStr := "123456789"
ts := Timestamp{}
err := ts.ParseTime(originalStr)
if err != nil {
panic(err)
}
result := ts.FormatTime()
if result != originalStr {
t.Errorf("expected %s got %s", originalStr, result)
}
}
func TestOSFamily(t *testing.T) {
osc := OSClass{
Family: "Linux",
}
if osc.OSFamily() != family.Linux {
t.Errorf("expected OSClass.OSFamily() to be equal to %v, got %v", family.Linux, osc.OSFamily())
}
}
func TestParseTableXML(t *testing.T) {
expectedTable := map[string]string{
"key": "AAAAB3NzaC1yc2EAAAABIwAAAQEAwVKoTY/7GFG7BmKkG6qFAHY/f3ciDX2MXTBLMEJP0xyUJsoy/CVRYw2b4qUB/GCJ5lh2InP+LVnPD3ZdtpyIvbS0eRZs/BH+mVLGh9xA/wOEUiiCfzQRsHj1xn7cqeWViAzQtdGluk/5CVAvr1FU3HNaaWkg7KQOSiKAzgDwCBtQhlgI40xdXgbqMkrHeP4M1p4MxoEVpZMe4oObACWwazeHP/Xas1vy5rbnmE59MpEZaA8t7AfGlW4MrVMhAB1JsFMdd0qFLpy/l93H3ptSlx1+6PQ5gUyjhmDUjMR+k6fb0yOeGdOrjN8IrWPmebZRFBjK5aCJwubgY/03VsSBMQ==",
"fingerprint": "79f809acd4e232421049d3bd208285ec",
"type": "ssh-rsa",
"bits": "2048",
}
input := []byte(fmt.Sprintf(
`<table><elem key="key">%s</elem><elem key="fingerprint">%s</elem><elem key="type">%s</elem><elem key="bits">%s</elem></table>`,
expectedTable["key"],
expectedTable["fingerprint"],
expectedTable["type"],
expectedTable["bits"],
))
var table Table
err := xml.Unmarshal(input, &table)
if err != nil {
panic(err)
}
if table["key"] != expectedTable["key"] {
t.Errorf("expected %v got %v", expectedTable["key"], table["key"])
}
if table["fingerprint"] != expectedTable["fingerprint"] {
t.Errorf("expected %v got %v", expectedTable["fingerprint"], table["fingerprint"])
}
if table["type"] != expectedTable["type"] {
t.Errorf("expected %v got %v", expectedTable["type"], table["type"])
}
if table["bits"] != expectedTable["bits"] {
t.Errorf("expected %v got %v", expectedTable["bits"], table["bits"])
}
}
func TestFormatTableXML(t *testing.T) {
table := Table(map[string]string{
"key": "AAAAB3NzaC1yc2EAAAABIwAAAQEAwVKoTY/7GFG7BmKkG6qFAHY/f3ciDX2MXTBLMEJP0xyUJsoy/CVRYw2b4qUB/GCJ5lh2InP+LVnPD3ZdtpyIvbS0eRZs/BH+mVLGh9xA/wOEUiiCfzQRsHj1xn7cqeWViAzQtdGluk/5CVAvr1FU3HNaaWkg7KQOSiKAzgDwCBtQhlgI40xdXgbqMkrHeP4M1p4MxoEVpZMe4oObACWwazeHP/Xas1vy5rbnmE59MpEZaA8t7AfGlW4MrVMhAB1JsFMdd0qFLpy/l93H3ptSlx1+6PQ5gUyjhmDUjMR+k6fb0yOeGdOrjN8IrWPmebZRFBjK5aCJwubgY/03VsSBMQ==",
"fingerprint": "79f809acd4e232421049d3bd208285ec",
"type": "ssh-rsa",
"bits": "2048",
})
expectedXML := [][]byte{
[]byte("<Table>"),
[]byte(fmt.Sprintf(`<elem key="key">%s</elem>`, table["key"])),
[]byte(fmt.Sprintf(`<elem key="fingerprint">%s</elem>`, table["fingerprint"])),
[]byte(fmt.Sprintf(`<elem key="type">%s</elem>`, table["type"])),
[]byte(fmt.Sprintf(`<elem key="bits">%s</elem>`, table["bits"])),
[]byte("</Table>"),
}
XML, err := xml.Marshal(table)
if err != nil {
panic(err)
}
for _, expectedXMLElement := range expectedXML {
if !bytes.Contains(XML, expectedXMLElement) {
t.Errorf("missing %s in %s", expectedXMLElement, XML)
}
}
}
func TestParseRunXML(t *testing.T) {
tests := []struct {
inputFile string
expectedResult *Run
expectedError error
}{
{
inputFile: "test_xml/scan01.xml",
expectedResult: &Run{
Args: "nmap -A -v -oX sample-03.xml freshmeat.net sourceforge.net nmap.org kernel.org openbsd.org netbsd.org google.com gmail.com",
Scanner: "nmap",
StartStr: "Sun Jan 27 21:10:02 2008",
Version: "4.53",
XMLOutputVersion: "1.01",
ScanInfo: ScanInfo{
NumServices: 1714,
Protocol: "tcp",
Services: "1-1027,1029-1033,1040,1043,1050,1058-1059,1067-1068,1076,1080,1083-1084,1103,1109-1110,1112,1127,1139,1155,1158,1178,1212,1214,1220,1222,1234,1241,1248,1270,1337,1346-1381,1383-1552,1600,1650-1652,1661-1672,1680,1720,1723,1755,1761-1764,1827,1900,1935,1984,1986-2028,2030,2032-2035,2038,2040-2049,2053,2064-2065,2067-2068,2105-2106,2108,2111-2112,2120-2121,2201,2232,2241,2301,2307,2401,2430-2433,2500-2501,2564,2600-2605,2627-2628,2638,2766,2784,2809,2903,2998,3000-3001,3005-3006,3025,3045,3049,3052,3064,3086,3128,3141,3264,3268-3269,3292,3299,3306,3333,3372,3389,3397-3399,3421,3455-3457,3462,3531,3632,3689,3900,3984-3986,3999-4000,4002,4008,4045,4125,4132-4133,4144,4199,4224,4321,4333,4343,4444,4480,4500,4557,4559,4660,4662,4672,4899,4987,4998,5000-5003,5009-5011,5050,5060,5100-5102,5145,5190-5193,5232,5236,5300-5305,5308,5400,5405,5432,5490,5500,5510,5520,5530,5540,5550,5555,5560,5631-5632,5679-5680,5713-5717,5800-5803,5900-5903,5977-5979,5997-6009,6017,6050,6101,6103,6105-6106,6110-6112,6141-6148,6222,6346-6347,6400-6401,6502,6543-6544,6547-6548,6558,6588,6662,6665-6670,6699-6701,6881,6969,7000-7010,7070,7100,7200-7201,7273,7326,7464,7597,7937-7938,8000,8007,8009,8021,8076,8080-8082,8118,8123,8443,8770,8888,8892,9040,9050-9051,9090,9100-9107,9111,9152,9535,9876,9991-9992,9999-10000,10005,10082-10083,11371,12000,12345-12346,13701-13702,13705-13706,13708-13718,13720-13722,13782-13783,14141,15126,15151,16080,16444,16959,17007,17300,18000,18181-18185,18187,19150,20005,22273,22289,22305,22321,22370,26208,27000-27010,27374,27665,31337,31416,32770-32780,32786-32787,38037,38292,43188,44334,44442-44443,47557,49400,50000,50002,54320,61439-61441,65301",
Type: "syn",
},
Start: Timestamp(time.Unix(1201479002, 0)),
Verbose: Verbose{
Level: 1,
},
Stats: Stats{
Finished: Finished{
Time: Timestamp(time.Unix(1201481569, 0)),
TimeStr: "Sun Jan 27 21:52:49 2008",
},
Hosts: HostStats{
Up: 8,
Total: 8,
Down: 0,
},
},
Hosts: []Host{
{
IPIDSequence: IPIDSequence{
Class: "All zeros",
Values: "0,0,0,0,0,0",
},
OS: OS{
PortsUsed: []PortUsed{
{
State: "open",
Proto: "tcp",
ID: 80,
},
{
State: "closed",
Proto: "tcp",
ID: 443,
},
},
Matches: []OSMatch{
{
Name: "MicroTik RouterOS 2.9.46",
Accuracy: 94,
Line: 14788,
},
{
Name: "Linksys WRT54GS WAP (Linux kernel)",
Accuracy: 94,
Line: 8292,
},
{
Name: "Linux 2.4.18 - 2.4.32 (likely embedded)",
Accuracy: 94,
Line: 8499,
},
{
Name: "Linux 2.4.21 - 2.4.33",
Accuracy: 94,
Line: 8624,
},
{
Name: "Linux 2.4.27",
Accuracy: 94,
Line: 8675,
},
{
Name: "Linux 2.4.28 - 2.4.30",
Accuracy: 94,
Line: 8693,
},
{
Name: "Linux 2.6.5 - 2.6.18",
Accuracy: 94,
Line: 11411,
},
{
Name: "Linux 2.6.8",
Accuracy: 94,
Line: 11485,
},
{
Name: "WebVOIZE 120 IP phone",
Accuracy: 94,
Line: 18921,
},
{
Name: "Linux 2.4.2 (Red Hat 7.1)",
Accuracy: 91,
Line: 8533,
},
},
Fingerprints: []OSFingerprint{
{
Fingerprint: fingerprint,
},
},
Classes: []OSClass{
{
Vendor: "MikroTik",
OSGeneration: "2.X",
Type: "software router",
Accuracy: 94,
Family: "RouterOS",
},
{
Vendor: "Linksys",
OSGeneration: "2.4.X",
Type: "WAP",
Accuracy: 94,
Family: "Linux",
},
{
Vendor: "Linux",
OSGeneration: "2.4.X",
Type: "general purpose",
Accuracy: 94,
Family: "Linux",
},
{
Vendor: "Linux",
OSGeneration: "2.6.X",
Type: "general purpose",
Accuracy: 94,
Family: "Linux",
},
{
Vendor: "WebVOIZE",
Type: "VoIP phone",
Accuracy: 94,
Family: "embedded",
},
{
Vendor: "D-Link",
OSGeneration: "2.4.X",
Type: "WAP",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "Inventel",
Type: "WAP",
Accuracy: 91,
Family: "embedded",
},
{
Vendor: "USRobotics",
Type: "broadband router",
Accuracy: 91,
Family: "embedded",
},
{
Vendor: "Linux",
OSGeneration: "2.4.X",
Type: "broadband router",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "Linux",
OSGeneration: "2.4.X",
Type: "WAP",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "Linux",
OSGeneration: "2.4.X",
Type: "media device",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "Linux",
OSGeneration: "2.4.X",
Type: "VoIP gateway",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "Netgear",
Type: "WAP",
Accuracy: 91,
Family: "embedded",
},
{
Vendor: "QLogic",
Type: "switch",
Accuracy: 91,
Family: "embedded",
},
{
Vendor: "Sharp",
OSGeneration: "2.4.X",
Type: "PDA",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "FON",
OSGeneration: "2.6.X",
Type: "WAP",
Accuracy: 91,
Family: "Linux",
},
{
Vendor: "FON",
OSGeneration: "2.4.X",
Type: "WAP",
Accuracy: 90,
Family: "Linux",
},
{
Vendor: "Belkin",
Type: "WAP",
Accuracy: 90,
Family: "embedded",
},
{
Vendor: "Asus",
Type: "WAP",
Accuracy: 90,
Family: "embedded",
},
{
Vendor: "Netgear",
OSGeneration: "2.4.X",
Type: "WAP",
Accuracy: 90,
Family: "Linux",
},
{
Vendor: "Xerox",
Type: "printer",
Accuracy: 90,
Family: "embedded",
},
{
Vendor: "Aladdin",
OSGeneration: "2.4.X",
Type: "security-misc",
Accuracy: 89,
Family: "Linux",
},
{
Vendor: "Occam",
Type: "VoIP gateway",
Accuracy: 89,
Family: "embedded",
},
{
Vendor: "Roku",
Type: "media device",
Accuracy: 89,
Family: "embedded",
},
{
Vendor: "Siemens",
Type: "WAP",
Accuracy: 89,
Family: "Linux",
},
{
Vendor: "3Com",
OSGeneration: "2.4.X",
Type: "broadband router",
Accuracy: 89,
Family: "Linux",
},
{
Vendor: "Dream Multimedia",
OSGeneration: "2.6.X",
Type: "media device",
Accuracy: 89,
Family: "Linux",
},
{
Vendor: "Iomega",
OSGeneration: "2.6.X",
Type: "storage-misc",
Accuracy: 89,
Family: "Linux",
},
},
},
Status: Status{
State: "up",
Reason: "reset",
},
TCPSequence: TCPSequence{
Index: 242,
Difficulty: "Good luck!",
Values: "457B276,4584FC8,161C122C,161B185F,1605EA95,1614C498",
},
TCPTSSequence: TCPTSSequence{
Class: "other",
Values: "3FB03AA9,3FB03C75,45B26360,45B2636A,45B26374,45B2637E",
},
Times: Times{
SRTT: "269788",
RTT: "41141",
To: "434352",
},
Trace: Trace{
Proto: "tcp",
Port: 80,
Hops: []Hop{
{
TTL: 1,
RTT: "1.83",
IPAddr: "192.168.254.254",
},
{
TTL: 2,
RTT: "18.95",
IPAddr: "200.217.89.32",
},
{
TTL: 3,
RTT: "18.33",
IPAddr: "200.217.30.250",
Host: "gigabitethernet5-1.80-cto-rn-rotd-02.telemar.net.br",
},
{
TTL: 4,
RTT: "45.05",
IPAddr: "200.97.65.250",
Host: "pos15-1-nbv-pe-rotd-03.telemar.net.br",
},
{
TTL: 5,
RTT: "43.49",
IPAddr: "200.223.131.13",
Host: "pos6-0-nbv-pe-rotn-01.telemar.net.br",
},
{
TTL: 6,
RTT: "91.27",
IPAddr: "200.223.131.205",
Host: "so-0-2-0-0-arc-rj-rotn-01.telemar.net.br",
},
{
TTL: 8,
RTT: "191.87",
IPAddr: "200.223.131.110",
Host: "PO0-3.ARC-RJ-ROTN-01.telemar.net.br",
},
{
TTL: 9,
RTT: "177.30",
IPAddr: "208.173.90.89",
Host: "bpr2-so-5-2-0.miamimit.savvis.net",
},
{
TTL: 10,
RTT: "181.50",
IPAddr: "208.172.97.169",
Host: "cr2-pos-0-3-1-0.miami.savvis.net",
},
{
TTL: 11,
RTT: "336.43",
IPAddr: "206.24.210.70",
Host: "cr1-loopback.sfo.savvis.net",
},
{
TTL: 12,
RTT: "245.32",
IPAddr: "204.70.200.229",
Host: "er1-te-1-0-1.SanJose3Equinix.savvis.net",
},
{
TTL: 13,
RTT: "238.47",
IPAddr: "204.70.200.210",
Host: "hr1-te-2-0-0.santaclarasc4.savvis.net",
},
{
TTL: 14,
RTT: "322.90",
IPAddr: "204.70.200.217",
Host: "hr1-te-2-0-0.santaclarasc9.savvis.net",
},
{
TTL: 15,
RTT: "330.96",
IPAddr: "204.70.203.146",
},
{
TTL: 16,
RTT: "342.57",
IPAddr: "66.35.194.59",
Host: "csr2-ve242.santaclarasc8.savvis.net",
},
{
TTL: 17,
RTT: "248.22",
IPAddr: "66.35.210.202",
},
{
TTL: 18,
RTT: "238.36",
IPAddr: "66.35.250.168",
Host: "freshmeat.net",
},
},
},
Uptime: Uptime{
Seconds: 206,
Lastboot: "Sun Jan 27 21:43:11 2008",
},
Addresses: []Address{
{
Addr: "66.35.250.168",
AddrType: "ipv4",
},
},
ExtraPorts: []ExtraPort{
{
State: "filtered",
Count: 1712,
Reasons: []Reason{
{
Reason: "host-prohibiteds",
Count: 1712,
},
},
},
},
Hostnames: []Hostname{
{
Name: "freshmeat.net",
Type: "PTR",
},
},
Ports: []Port{
{
ID: 80,
Protocol: "tcp",
Service: Service{
Name: "http",
ExtraInfo: "(Unix) PHP/4.4.7",
Method: "probed",
Product: "Apache httpd",
Version: "1.3.39",
Configuration: 10,
},
State: State{
State: "open",
Reason: "syn-ack",
ReasonTTL: 45,
},
Scripts: []Script{
{
ID: "robots.txt",
Output: "User-Agent: * /img/ /redir/ ",
},
{
ID: "HTML title",
Output: "freshmeat.net: Welcome to freshmeat.net",
},
},
},
{
ID: 443,
Protocol: "tcp",
Service: Service{
Name: "https",
Method: "table",
Configuration: 3,
},
State: State{
State: "closed",
Reason: "reset",
ReasonTTL: 46,
},
},
},
},
},
TaskBegin: []Task{
{
Time: Timestamp(time.Unix(1201479013, 0)),
Task: "Ping Scan",
},
{
Time: Timestamp(time.Unix(1201479014, 0)),
Task: "Parallel DNS resolution of 8 hosts.",
},
{
Time: Timestamp(time.Unix(1201479015, 0)),
Task: "System CNAME DNS resolution of 4 hosts.",
},
{
Time: Timestamp(time.Unix(1201479016, 0)),
Task: "SYN Stealth Scan",
},
{
Time: Timestamp(time.Unix(1201480879, 0)),
Task: "Service scan",
},
{
Time: Timestamp(time.Unix(1201481006, 0)),
Task: "Traceroute",
},
{
Time: Timestamp(time.Unix(1201481028, 0)),
Task: "Traceroute",
},
{
Time: Timestamp(time.Unix(1201481059, 0)),
Task: "Parallel DNS resolution of 85 hosts.",
},
{
Time: Timestamp(time.Unix(1201481070, 0)),
Task: "System CNAME DNS resolution of 8 hosts.",
},
{
Time: Timestamp(time.Unix(1201481086, 0)),
Task: "SCRIPT ENGINE",
},
},
TaskProgress: []TaskProgress{
{
Percent: 3.22,
Remaining: 903,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201479949, 0)),
Time: Timestamp(time.Unix(1201479046, 0)),
},
{
Percent: 56.66,
Remaining: 325,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201479767, 0)),
Time: Timestamp(time.Unix(1201479442, 0)),
},
{
Percent: 77.02,
Remaining: 225,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201479995, 0)),
Time: Timestamp(time.Unix(1201479770, 0)),
},
{
Percent: 81.95,
Remaining: 215,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480212, 0)),
Time: Timestamp(time.Unix(1201479996, 0)),
},
{
Percent: 86.79,
Remaining: 182,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480395, 0)),
Time: Timestamp(time.Unix(1201480213, 0)),
},
{
Percent: 87.84,
Remaining: 172,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480433, 0)),
Time: Timestamp(time.Unix(1201480260, 0)),
},
{
Percent: 91.65,
Remaining: 129,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480564, 0)),
Time: Timestamp(time.Unix(1201480435, 0)),
},
{
Percent: 94.43,
Remaining: 91,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480656, 0)),
Time: Timestamp(time.Unix(1201480565, 0)),
},
{
Percent: 96.35,
Remaining: 62,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480720, 0)),
Time: Timestamp(time.Unix(1201480658, 0)),
},
{
Percent: 97.76,
Remaining: 39,
Task: "SYN Stealth Scan",
Etc: Timestamp(time.Unix(1201480760, 0)),
Time: Timestamp(time.Unix(1201480721, 0)),
},
},
TaskEnd: []Task{
{
Time: Timestamp(time.Unix(1201479014, 0)),
Task: "Ping Scan",
ExtraInfo: "8 total hosts",
},
{
Time: Timestamp(time.Unix(1201479015, 0)),
Task: "Parallel DNS resolution of 8 hosts.",
},
{
Time: Timestamp(time.Unix(1201479016, 0)),
Task: "System CNAME DNS resolution of 4 hosts.",
},
{
Time: Timestamp(time.Unix(1201480878, 0)),
Task: "SYN Stealth Scan",
ExtraInfo: "8570 total ports",
},
{
Time: Timestamp(time.Unix(1201480984, 0)),
Task: "Service scan",
ExtraInfo: "20 services on 5 hosts",
},
{
Time: Timestamp(time.Unix(1201481028, 0)),
Task: "Traceroute",
},
{
Time: Timestamp(time.Unix(1201481059, 0)),
Task: "Traceroute",
},
{
Time: Timestamp(time.Unix(1201481070, 0)),
Task: "Parallel DNS resolution of 85 hosts.",
},
{
Time: Timestamp(time.Unix(1201481086, 0)),
Task: "System CNAME DNS resolution of 8 hosts.",
},
{
Time: Timestamp(time.Unix(1201481197, 0)),
Task: "SCRIPT ENGINE",
},
},
},
expectedError: nil,
},
}
for _, test := range tests {
t.Run(test.inputFile, func(t *testing.T) {
rawXML, err := ioutil.ReadFile(test.inputFile)
if err != nil {
t.Fatal(err)
}
result, err := Parse(rawXML)
// Remove rawXML before comparing
result.rawXML = []byte{}
compareResults(t, test.expectedResult, result)
if err != test.expectedError {
t.Errorf("expected %v got %v", test.expectedError, err)
}
})
}
}
func compareResults(t *testing.T, expected, got *Run) {
if got.Args != expected.Args {
t.Errorf("unexpected arguments, expected %v got %v", expected.Args, got.Args)
}
if got.ProfileName != expected.ProfileName {
t.Errorf("unexpected arguments, expected %v got %v", expected.ProfileName, got.ProfileName)
}
if got.Scanner != expected.Scanner {
t.Errorf("unexpected arguments, expected %v got %v", expected.Scanner, got.Scanner)
}
if got.StartStr != expected.StartStr {
t.Errorf("unexpected arguments, expected %v got %v", expected.StartStr, got.StartStr)
}
if !reflect.DeepEqual(got.Debugging, expected.Debugging) {
t.Errorf("unexpected debugging, expected %+v got %+v", expected.Debugging, got.Debugging)
}
if !reflect.DeepEqual(got.ScanInfo, expected.ScanInfo) {
t.Errorf("unexpected scan info, expected %+v got %+v", expected.ScanInfo, got.ScanInfo)
}
if !reflect.DeepEqual(got.Start, expected.Start) {
t.Errorf("unexpected start time, expected %+v got %+v", expected.Start, got.Start)
}
if !reflect.DeepEqual(got.Targets, expected.Targets) {
t.Errorf("unexpected targets, expected %+v got %+v", expected.Targets, got.Targets)
}
if len(expected.TaskBegin) != len(got.TaskBegin) {
t.Errorf("unexpected tasks begin entries, expected to have %d entries, got %d instead", len(expected.TaskBegin), len(got.TaskBegin))
} else {
for idx := range expected.TaskBegin {
if !reflect.DeepEqual(got.TaskBegin[idx], expected.TaskBegin[idx]) {
t.Errorf("unexpected task begin entry, expected %+v got %+v", expected.TaskBegin[idx], got.TaskBegin[idx])
}
}
}
if len(expected.TaskProgress) != len(got.TaskProgress) {
t.Errorf("unexpected tasks progress entries, expected to have %d entries, got %d instead", len(expected.TaskProgress), len(got.TaskProgress))
} else {
for idx := range expected.TaskProgress {
if !reflect.DeepEqual(got.TaskProgress[idx], expected.TaskProgress[idx]) {
t.Errorf("unexpected task progress entry, expected %+v got %+v", expected.TaskProgress[idx], got.TaskProgress[idx])
}
}
}
if len(expected.TaskEnd) != len(got.TaskEnd) {
t.Errorf("unexpected tasks end entries, expected to have %d entries, got %d instead", len(expected.TaskEnd), len(got.TaskEnd))
} else {
for idx := range expected.TaskEnd {
if !reflect.DeepEqual(got.TaskEnd[idx], expected.TaskEnd[idx]) {
t.Errorf("unexpected task end entry, expected %+v got %+v", expected.TaskEnd[idx], got.TaskEnd[idx])
}
}
}
if len(expected.Hosts) != len(got.Hosts) {
t.Errorf("unexpected number of hosts, expected to have %d hosts, got %d instead", len(expected.Hosts), len(got.Hosts))
} else {
for idx := range expected.Hosts {
if expected.Hosts[idx].Comment != got.Hosts[idx].Comment {
t.Errorf("unexpected host comment, expected %v got %v", expected.Hosts[idx].Comment, got.Hosts[idx].Comment)
}
if !reflect.DeepEqual(expected.Hosts[idx].Addresses, got.Hosts[idx].Addresses) {
t.Errorf("unexpected host addresses, expected %+v got %+v", expected.Hosts[idx].Addresses, got.Hosts[idx].Addresses)
}
if !reflect.DeepEqual(expected.Hosts[idx].Distance, got.Hosts[idx].Distance) {
t.Errorf("unexpected host distance, expected %+v got %+v", expected.Hosts[idx].Distance, got.Hosts[idx].Distance)
}
if !reflect.DeepEqual(expected.Hosts[idx].ExtraPorts, got.Hosts[idx].ExtraPorts) {
t.Errorf("unexpected host extra ports, expected %+v got %+v", expected.Hosts[idx].ExtraPorts, got.Hosts[idx].ExtraPorts)
}
if !reflect.DeepEqual(expected.Hosts[idx].HostScripts, got.Hosts[idx].HostScripts) {
t.Errorf("unexpected host host scripts, expected %+v got %+v", expected.Hosts[idx].HostScripts, got.Hosts[idx].HostScripts)
}
if !reflect.DeepEqual(expected.Hosts[idx].Hostnames, got.Hosts[idx].Hostnames) {
t.Errorf("unexpected host host names, expected %+v got %+v", expected.Hosts[idx].Hostnames, got.Hosts[idx].Hostnames)
}
if !reflect.DeepEqual(expected.Hosts[idx].IPIDSequence, got.Hosts[idx].IPIDSequence) {
t.Errorf("unexpected host IPIDSequence, expected %+v got %+v", expected.Hosts[idx].IPIDSequence, got.Hosts[idx].IPIDSequence)
}
if !reflect.DeepEqual(expected.Hosts[idx].OS, got.Hosts[idx].OS) {
t.Errorf("unexpected host OS, expected %+v got %+v", expected.Hosts[idx].OS, got.Hosts[idx].OS)
}
if !reflect.DeepEqual(expected.Hosts[idx].Ports, got.Hosts[idx].Ports) {
t.Errorf("unexpected host ports, expected %+v got %+v", expected.Hosts[idx].Ports, got.Hosts[idx].Ports)
}
if !reflect.DeepEqual(expected.Hosts[idx].Smurfs, got.Hosts[idx].Smurfs) {
t.Errorf("unexpected host smurfs, expected %+v got %+v", expected.Hosts[idx].Smurfs, got.Hosts[idx].Smurfs)
}
if !reflect.DeepEqual(expected.Hosts[idx].StartTime, got.Hosts[idx].StartTime) {
t.Errorf("unexpected host start time, expected %+v got %+v", expected.Hosts[idx].StartTime, got.Hosts[idx].StartTime)
}
if !reflect.DeepEqual(expected.Hosts[idx].Status, got.Hosts[idx].Status) {
t.Errorf("unexpected host status, expected %+v got %+v", expected.Hosts[idx].Status, got.Hosts[idx].Status)
}
if !reflect.DeepEqual(expected.Hosts[idx].TCPSequence, got.Hosts[idx].TCPSequence) {
t.Errorf("unexpected host TCPSequence, expected %+v got %+v", expected.Hosts[idx].TCPSequence, got.Hosts[idx].TCPSequence)
}
if !reflect.DeepEqual(expected.Hosts[idx].TCPTSSequence, got.Hosts[idx].TCPTSSequence) {
t.Errorf("unexpected host TCPTSSequence, expected %+v got %+v", expected.Hosts[idx].TCPTSSequence, got.Hosts[idx].TCPTSSequence)
}
if !reflect.DeepEqual(expected.Hosts[idx].Times, got.Hosts[idx].Times) {
t.Errorf("unexpected host times, expected %+v got %+v", expected.Hosts[idx].Times, got.Hosts[idx].Times)
}
if !reflect.DeepEqual(expected.Hosts[idx].Trace, got.Hosts[idx].Trace) {
t.Errorf("unexpected host trace, expected %+v got %+v", expected.Hosts[idx].Trace, got.Hosts[idx].Trace)
}
if !reflect.DeepEqual(expected.Hosts[idx].Uptime, got.Hosts[idx].Uptime) {
t.Errorf("unexpected host uptime, expected %+v got %+v", expected.Hosts[idx].Uptime, got.Hosts[idx].Uptime)
}
}
}
}
const fingerprint = "SCAN(V=4.53%D=1/27%OT=80%CT=443%CU=%PV=N%G=N%TM=479D25ED%P=i686-pc-linux-gnu)\nSEQ(SP=F2%GCD=1%ISR=E9%TI=Z%TS=1C)\nOPS(O1=M5B4ST11NW0%O2=M5B4ST11NW0%O3=M5B4NNT11NW0%O4=M5B4ST11NW0%O5=M5B4ST11NW0%O6=M5B4ST11)\nWIN(W1=16A0%W2=16A0%W3=16A0%W4=16A0%W5=16A0%W6=16A0)\nECN(R=Y%DF=Y%TG=40%W=16D0%O=M5B4NNSNW0%CC=N%Q=)\nT1(R=Y%DF=Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)\nT2(R=N)\nT3(R=Y%DF=Y%TG=40%W=16A0%S=O%A=S+%F=AS%O=M5B4ST11NW0%RD=0%Q=)\nT4(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)\nT5(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)\nT6(R=Y%DF=Y%TG=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)\nT7(R=Y%DF=Y%TG=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)\nU1(R=N)\nIE(R=N)\n"
-4
View File
@@ -1,4 +0,0 @@
*.6
*.o
src/_cgo_*
src/_obj/
-16
View File
@@ -1,16 +0,0 @@
language: go
go:
- 1.9
- 1.8
- 1.7
- 1.6
before_install:
- sudo apt-get update -qq > apt-get.out 2>&1 || (cat apt-get.out && exit 1)
install: go build -x -v
script: go test -v
notifications:
email:
recipients:
- fledna@foxmail.com
on_success: change
on_failure: always
-13
View File
@@ -1,13 +0,0 @@
Copyright 2014 Shuyu Wang <andelf@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this project except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-73
View File
@@ -1,73 +0,0 @@
go-curl
=======
[![Build Status](https://secure.travis-ci.org/andelf/go-curl.png?branch=master)](http://travis-ci.org/andelf/go-curl)
my golang libcurl(curl) binding.
See more examples in ./examples/ directory~!
LICENSE
-------
go-curl is licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html).
Current Development Status
--------------------------
* currently stable
* READ, WRITE, HEADER, PROGRESS function callback
* a Multipart Form supports file uploading
* Most curl_easy_setopt option
* partly implement share & multi interface
* new callback function prototype
How to Install
--------------
Make Sure You Have libcurl (and its develop headers, static/dynamic libs) installed!
$ go get -u github.com/andelf/go-curl
Current Status
--------------
* Linux x64
* passed go1 (ArchLinux)
* Windows x86
* passed go1 (win7, mingw-gcc 4.5.2, curl 7.22.0)
* Mac OS
* passed go1 (Mac OS X 10.7.3, curl 7.21.4)
Sample Program
--------------
```go
package main
import (
"fmt"
curl "github.com/andelf/go-curl"
)
func main() {
easy := curl.EasyInit()
defer easy.Cleanup()
easy.Setopt(curl.OPT_URL, "http://www.baidu.com/")
// make a callback function
fooTest := func (buf []byte, userdata interface{}) bool {
println("DEBUG: size=>", len(buf))
println("DEBUG: content=>", string(buf))
return true
}
easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
if err := easy.Perform(); err != nil {
fmt.Printf("ERROR: %v\n", err)
}
}
```
-42
View File
@@ -1,42 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "callback.h"
#include "_cgo_export.h"
/* for OPT_HEADERFUNCTION */
size_t header_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
return goCallHeaderFunction(ptr, size*nmemb, ctx);
}
void *return_header_function() {
return (void *)&header_function;
}
/* for OPT_WRITEFUNCTION */
size_t write_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
return goCallWriteFunction(ptr, size*nmemb, ctx);
}
void *return_write_function() {
return (void *)&write_function;
}
/* for OPT_READFUNCTION */
size_t read_function( char *ptr, size_t size, size_t nmemb, void *ctx) {
return goCallReadFunction(ptr, size*nmemb, ctx);
}
void *return_read_function() {
return (void *)&read_function;
}
/* for OPT_PROGRESSFUNCTION */
int progress_function(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow) {
return goCallProgressFunction(dltotal, dlnow, ultotal, ulnow, ctx);
}
void *return_progress_function() {
return (void *)progress_function;
}
-59
View File
@@ -1,59 +0,0 @@
package curl
/*
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo freebsd LDFLAGS: -L/usr/local/lib -lcurl
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
*/
import "C"
import (
"unsafe"
)
//export goCallHeaderFunction
func goCallHeaderFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
curl := context_map.Get(uintptr(ctx))
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
if (*curl.headerFunction)(buf, curl.headerData) {
return uintptr(size)
}
return C.CURL_WRITEFUNC_PAUSE
}
//export goCallWriteFunction
func goCallWriteFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
curl := context_map.Get(uintptr(ctx))
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
if (*curl.writeFunction)(buf, curl.writeData) {
return uintptr(size)
}
return C.CURL_WRITEFUNC_PAUSE
}
//export goCallProgressFunction
func goCallProgressFunction(dltotal, dlnow, ultotal, ulnow C.double, ctx unsafe.Pointer) int {
curl := context_map.Get(uintptr(ctx))
if (*curl.progressFunction)(float64(dltotal), float64(dlnow),
float64(ultotal), float64(ulnow),
curl.progressData) {
return 0
}
return 1
}
//export goCallReadFunction
func goCallReadFunction(ptr *C.char, size C.size_t, ctx unsafe.Pointer) uintptr {
curl := context_map.Get(uintptr(ctx))
buf := C.GoBytes(unsafe.Pointer(ptr), C.int(size))
ret := (*curl.readFunction)(buf, curl.readData)
str := C.CString(string(buf))
defer C.free(unsafe.Pointer(str))
if C.memcpy(unsafe.Pointer(ptr), unsafe.Pointer(str), C.size_t(ret)) == nil {
panic("read_callback memcpy error!")
}
return uintptr(ret)
}
-6
View File
@@ -1,6 +0,0 @@
void *return_header_function();
void *return_write_function();
void *return_read_function();
void *return_progress_function();
-528
View File
@@ -1,528 +0,0 @@
/* generated by compatgen.py */
#include<curl/curl.h>
#if (LIBCURL_VERSION_MINOR == 57 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 57
#define CURL_VERSION_BROTLI 0
#if (LIBCURL_VERSION_MINOR == 56 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 56
#if (LIBCURL_VERSION_MINOR == 56 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 56
#define CURLOPT_SSH_COMPRESSION 0
#define CURLOPT_MIMEPOST 0
#define CURL_VERSION_MULTI_SSL 0
#if (LIBCURL_VERSION_MINOR == 55 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 55
#if (LIBCURL_VERSION_MINOR == 55 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 55
#define CURLOPT_REQUEST_TARGET 0
#define CURLOPT_SOCKS5_AUTH 0
#define CURLINFO_SIZE_UPLOAD_T 0
#define CURLINFO_SIZE_DOWNLOAD_T 0
#define CURLINFO_SPEED_DOWNLOAD_T 0
#define CURLINFO_SPEED_UPLOAD_T 0
#define CURLINFO_CONTENT_LENGTH_DOWNLOAD_T 0
#define CURLINFO_CONTENT_LENGTH_UPLOAD_T 0
#define CURLAUTH_GSSAPI 0
#if (LIBCURL_VERSION_MINOR == 54 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 54
#if (LIBCURL_VERSION_MINOR == 54 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 54
#define CURLOPT_SUPPRESS_CONNECT_HEADERS 0
#if (LIBCURL_VERSION_MINOR == 53 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 53
#if (LIBCURL_VERSION_MINOR == 53 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 53
#define CURLOPT_ABSTRACT_UNIX_SOCKET 0
#if (LIBCURL_VERSION_MINOR == 52 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 52
#if (LIBCURL_VERSION_MINOR == 52 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 52
#define CURLOPT_PROXY_CAINFO 0
#define CURLOPT_PROXY_CAPATH 0
#define CURLOPT_PROXY_SSL_VERIFYPEER 0
#define CURLOPT_PROXY_SSL_VERIFYHOST 0
#define CURLOPT_PROXY_SSLVERSION 0
#define CURLOPT_PROXY_TLSAUTH_USERNAME 0
#define CURLOPT_PROXY_TLSAUTH_PASSWORD 0
#define CURLOPT_PROXY_TLSAUTH_TYPE 0
#define CURLOPT_PROXY_SSLCERT 0
#define CURLOPT_PROXY_SSLCERTTYPE 0
#define CURLOPT_PROXY_SSLKEY 0
#define CURLOPT_PROXY_SSLKEYTYPE 0
#define CURLOPT_PROXY_KEYPASSWD 0
#define CURLOPT_PROXY_SSL_CIPHER_LIST 0
#define CURLOPT_PROXY_CRLFILE 0
#define CURLOPT_PROXY_SSL_OPTIONS 0
#define CURLOPT_PRE_PROXY 0
#define CURLOPT_PROXY_PINNEDPUBLICKEY 0
#define CURLINFO_PROXY_SSL_VERIFYRESULT 0
#define CURLINFO_PROTOCOL 0
#define CURLINFO_SCHEME 0
#define CURL_VERSION_HTTPS_PROXY 0
#if (LIBCURL_VERSION_MINOR == 51 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 51
#define CURLOPT_KEEP_SENDING_ON_ERROR 0
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 50
#define CURLE_WEIRD_SERVER_REPLY -1
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 50
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 50
#if (LIBCURL_VERSION_MINOR == 50 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 50
#define CURLINFO_HTTP_VERSION 0
#if (LIBCURL_VERSION_MINOR == 49 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 49
#if (LIBCURL_VERSION_MINOR == 49 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 49
#define CURLOPT_CONNECT_TO 0
#define CURLOPT_TCP_FASTOPEN 0
#define CURLE_TOO_MANY_REDIRECTS -1
#define CURLE_TELNET_OPTION_SYNTAX -1
#define CURLE_HTTP2_STREAM -1
#if (LIBCURL_VERSION_MINOR == 48 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 48
#define CURLOPT_TFTP_NO_OPTIONS 0
#define CURLINFO_TLS_SSL_PTR 0
#if (LIBCURL_VERSION_MINOR == 47 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 47
#if (LIBCURL_VERSION_MINOR == 47 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 47
#define CURL_VERSION_PSL 0
#if (LIBCURL_VERSION_MINOR == 46 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 46
#define CURLOPT_STREAM_WEIGHT 0
#define CURLOPT_STREAM_DEPENDS 0
#define CURLOPT_STREAM_DEPENDS_E 0
#if (LIBCURL_VERSION_MINOR == 45 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 45
#define CURLOPT_DEFAULT_PROTOCOL 0
#define CURLINFO_ACTIVESOCKET 0
#if (LIBCURL_VERSION_MINOR == 44 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 44
#if (LIBCURL_VERSION_MINOR == 43 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 43
#define CURLOPT_PROXY_SERVICE_NAME 0
#define CURLOPT_SERVICE_NAME 0
#define CURLOPT_PIPEWAIT 0
#if (LIBCURL_VERSION_MINOR == 42 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 42
#if (LIBCURL_VERSION_MINOR == 42 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 42
#define CURLOPT_SSL_FALSESTART 0
#define CURLOPT_PATH_AS_IS 0
#if (LIBCURL_VERSION_MINOR == 41 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 41
#define CURLOPT_SSL_VERIFYSTATUS 0
#define CURLE_SSL_INVALIDCERTSTATUS -1
#if (LIBCURL_VERSION_MINOR == 40 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 40
#define CURLOPT_UNIX_SOCKET_PATH 0
#define CURL_VERSION_KERBEROS5 0
#define CURL_VERSION_UNIX_SOCKETS 0
#if (LIBCURL_VERSION_MINOR == 39 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 39
#define CURLOPT_PINNEDPUBLICKEY 0
#define CURLE_SSL_PINNEDPUBKEYNOTMATCH -1
#if (LIBCURL_VERSION_MINOR == 38 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 38
#define CURLE_HTTP2 -1
#define CURL_VERSION_GSSAPI 0
#define CURLAUTH_NEGOTIATE 0
#if (LIBCURL_VERSION_MINOR == 37 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 37
#define CURLOPT_OBSOLETE40 0
#define CURLOPT_OBSOLETE72 0
#if (LIBCURL_VERSION_MINOR == 37 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 37
#define CURLOPT_PROXYHEADER 0
#define CURLOPT_HEADEROPT 0
#if (LIBCURL_VERSION_MINOR == 36 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 36
#define CURLOPT_SSL_ENABLE_NPN 0
#define CURLOPT_SSL_ENABLE_ALPN 0
#define CURLOPT_EXPECT_100_TIMEOUT_MS 0
#if (LIBCURL_VERSION_MINOR == 35 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 35
#if (LIBCURL_VERSION_MINOR == 34 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 34
#define CURLOPT_LOGIN_OPTIONS 0
#define CURLINFO_TLS_SESSION 0
#if (LIBCURL_VERSION_MINOR == 33 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 33
#define CURLOPT_XOAUTH2_BEARER 0
#define CURLOPT_DNS_INTERFACE 0
#define CURLOPT_DNS_LOCAL_IP4 0
#define CURLOPT_DNS_LOCAL_IP6 0
#define CURL_VERSION_HTTP2 0
#if (LIBCURL_VERSION_MINOR == 32 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 32
#define CURLOPT_XFERINFODATA 0
#define CURLOPT_XFERINFOFUNCTION 0
#if (LIBCURL_VERSION_MINOR == 31 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 31
#define CURLOPT_SASL_IR 0
#if (LIBCURL_VERSION_MINOR == 30 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 30
#define CURLE_NO_CONNECTION_AVAILABLE -1
#if (LIBCURL_VERSION_MINOR == 29 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 29
#if (LIBCURL_VERSION_MINOR == 28 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 28
#if (LIBCURL_VERSION_MINOR == 28 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 28
#if (LIBCURL_VERSION_MINOR == 27 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 27
#if (LIBCURL_VERSION_MINOR == 26 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 26
#if (LIBCURL_VERSION_MINOR == 25 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 25
#define CURLOPT_TCP_KEEPALIVE 0
#define CURLOPT_TCP_KEEPIDLE 0
#define CURLOPT_TCP_KEEPINTVL 0
#define CURLOPT_SSL_OPTIONS 0
#define CURLOPT_MAIL_AUTH 0
#if (LIBCURL_VERSION_MINOR == 24 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 24
#define CURLOPT_DNS_SERVERS 0
#define CURLOPT_ACCEPTTIMEOUT_MS 0
#define CURLE_FTP_ACCEPT_FAILED -1
#define CURLE_FTP_ACCEPT_TIMEOUT -1
#if (LIBCURL_VERSION_MINOR == 23 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 23
#if (LIBCURL_VERSION_MINOR == 23 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 23
#if (LIBCURL_VERSION_MINOR == 22 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 22
#define CURLOPT_GSSAPI_DELEGATION 0
#define CURL_VERSION_NTLM_WB 0
#define CURLAUTH_NTLM_WB 0
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 21
#define CURLOPT_CLOSESOCKETFUNCTION 0
#define CURLOPT_CLOSESOCKETDATA 0
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 21
#define CURLOPT_ACCEPT_ENCODING 0
#define CURLOPT_TRANSFER_ENCODING 0
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 21
#define CURLE_NOT_BUILT_IN -1
#define CURLE_UNKNOWN_OPTION -1
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 21
#define CURLOPT_TLSAUTH_USERNAME 0
#define CURLOPT_TLSAUTH_PASSWORD 0
#define CURLOPT_TLSAUTH_TYPE 0
#define CURL_VERSION_TLSAUTH_SRP 0
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 21
#define CURLOPT_RESOLVE 0
#define CURLAUTH_ONLY 0
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 21
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 21
#if (LIBCURL_VERSION_MINOR == 21 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 21
#define CURLOPT_WILDCARDMATCH 0
#define CURLOPT_CHUNK_BGN_FUNCTION 0
#define CURLOPT_CHUNK_END_FUNCTION 0
#define CURLOPT_FNMATCH_FUNCTION 0
#define CURLOPT_CHUNK_DATA 0
#define CURLOPT_FNMATCH_DATA 0
#define CURLE_FTP_BAD_FILE_LIST -1
#define CURLE_CHUNK_FAILED -1
#define CURLINFO_PRIMARY_PORT 0
#define CURLINFO_LOCAL_IP 0
#define CURLINFO_LOCAL_PORT 0
#if (LIBCURL_VERSION_MINOR == 20 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 20
#if (LIBCURL_VERSION_MINOR == 20 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 20
#define CURLOPT_SERVER_RESPONSE_TIMEOUT 0
#define CURLOPT_MAIL_FROM 0
#define CURLOPT_MAIL_RCPT 0
#define CURLOPT_FTP_USE_PRET 0
#define CURLOPT_RTSP_REQUEST 0
#define CURLOPT_RTSP_SESSION_ID 0
#define CURLOPT_RTSP_STREAM_URI 0
#define CURLOPT_RTSP_TRANSPORT 0
#define CURLOPT_RTSP_CLIENT_CSEQ 0
#define CURLOPT_RTSP_SERVER_CSEQ 0
#define CURLOPT_INTERLEAVEDATA 0
#define CURLOPT_INTERLEAVEFUNCTION 0
#define CURLOPT_RTSPHEADER 0
#define CURLE_FTP_PRET_FAILED -1
#define CURLE_RTSP_CSEQ_ERROR -1
#define CURLE_RTSP_SESSION_ERROR -1
#define CURLINFO_RTSP_SESSION_ID 0
#define CURLINFO_RTSP_CLIENT_CSEQ 0
#define CURLINFO_RTSP_SERVER_CSEQ 0
#define CURLINFO_RTSP_CSEQ_RECV 0
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 19
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 19
#define CURLOPT_SSH_KNOWNHOSTS 0
#define CURLOPT_SSH_KEYFUNCTION 0
#define CURLOPT_SSH_KEYDATA 0
#define CURL_VERSION_CURLDEBUG 0
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 19
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 19
#define CURLOPT_NOPROXY 0
#define CURLOPT_TFTP_BLKSIZE 0
#define CURLOPT_SOCKS5_GSSAPI_SERVICE 0
#define CURLOPT_SOCKS5_GSSAPI_NEC 0
#define CURLOPT_PROTOCOLS 0
#define CURLOPT_REDIR_PROTOCOLS 0
#define CURLINFO_CONDITION_UNMET 0
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 19
#define CURLAUTH_DIGEST_IE 0
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 19
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 19
#define CURLOPT_POSTREDIR 0
#define CURLOPT_CERTINFO 0
#define CURLOPT_USERNAME 0
#define CURLOPT_PASSWORD 0
#define CURLOPT_PROXYUSERNAME 0
#define CURLOPT_PROXYPASSWORD 0
#define CURLINFO_CERTINFO 0
#if (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 19
#define CURLOPT_CRLFILE 0
#define CURLOPT_ISSUERCERT 0
#define CURLOPT_ADDRESS_SCOPE 0
#define CURLE_SSL_CRL_BADFILE -1
#define CURLE_SSL_ISSUER_ERROR -1
#define CURLINFO_PRIMARY_IP 0
#define CURLINFO_APPCONNECT_TIME 0
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 18
#define CURLE_AGAIN -1
#define CURLINFO_REDIRECT_URL 0
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 18
#if (LIBCURL_VERSION_MINOR == 18 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 18
#define CURLOPT_PROXY_TRANSFER_MODE 0
#define CURLOPT_SEEKFUNCTION 0
#define CURLOPT_SEEKDATA 0
#if (LIBCURL_VERSION_MINOR == 17 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 17
#define CURLOPT_POST301 0
#define CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 0
#define CURLOPT_OPENSOCKETFUNCTION 0
#define CURLOPT_OPENSOCKETDATA 0
#define CURLOPT_COPYPOSTFIELDS 0
#define CURLE_PEER_FAILED_VERIFICATION -1
#if (LIBCURL_VERSION_MINOR == 17 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 17
#define CURLOPT_LOW_SPEED_LIMIT 0
#define CURLOPT_KEYPASSWD 0
#define CURLOPT_DIRLISTONLY 0
#define CURLOPT_APPEND 0
#define CURLOPT_FTP_RESPONSE_TIMEOUT 0
#define CURLOPT_USE_SSL 0
#define CURLE_OBSOLETE4 -1
#define CURLE_REMOTE_ACCESS_DENIED -1
#define CURLE_OBSOLETE10 -1
#define CURLE_OBSOLETE12 -1
#define CURLE_OBSOLETE16 -1
#define CURLE_FTP_COULDNT_SET_TYPE -1
#define CURLE_OBSOLETE20 -1
#define CURLE_QUOTE_ERROR -1
#define CURLE_OBSOLETE24 -1
#define CURLE_OBSOLETE29 -1
#define CURLE_OBSOLETE32 -1
#define CURLE_RANGE_ERROR -1
#define CURLE_OBSOLETE40 -1
#define CURLE_OBSOLETE44 -1
#define CURLE_OBSOLETE46 -1
#define CURLE_OBSOLETE50 -1
#define CURLE_OBSOLETE57 -1
#define CURLE_USE_SSL_FAILED -1
#define CURLE_REMOTE_DISK_FULL -1
#define CURLE_REMOTE_FILE_EXISTS -1
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 16
#define CURLOPT_KRBLEVEL 0
#define CURLOPT_NEW_FILE_PERMS 0
#define CURLOPT_NEW_DIRECTORY_PERMS 0
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 16
#define CURLE_UPLOAD_FAILED -1
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 16
#define CURLOPT_TIMEOUT_MS 0
#define CURLOPT_CONNECTTIMEOUT_MS 0
#define CURLOPT_HTTP_TRANSFER_DECODING 0
#define CURLOPT_HTTP_CONTENT_DECODING 0
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 16
#define CURLOPT_SSH_AUTH_TYPES 0
#define CURLOPT_SSH_PUBLIC_KEYFILE 0
#define CURLOPT_SSH_PRIVATE_KEYFILE 0
#define CURLOPT_FTP_SSL_CCC 0
#define CURLE_REMOTE_FILE_NOT_FOUND -1
#define CURLE_SSH -1
#define CURLE_SSL_SHUTDOWN_FAILED -1
#if (LIBCURL_VERSION_MINOR == 16 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 16
#define CURLOPT_SOCKOPTFUNCTION 0
#define CURLOPT_SOCKOPTDATA 0
#define CURLOPT_SSL_SESSIONID_CACHE 0
#define CURLE_SSL_CACERT_BADFILE -1
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 15
#define CURLOPT_MAX_SEND_SPEED_LARGE 0
#define CURLOPT_MAX_RECV_SPEED_LARGE 0
#define CURLOPT_FTP_ALTERNATIVE_TO_USER 0
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 15
#define CURLOPT_CONV_FROM_NETWORK_FUNCTION 0
#define CURLOPT_CONV_TO_NETWORK_FUNCTION 0
#define CURLOPT_CONV_FROM_UTF8_FUNCTION 0
#define CURLE_CONV_FAILED -1
#define CURLE_CONV_REQD -1
#define CURLINFO_FTP_ENTRY_PATH 0
#define CURL_VERSION_CONV 0
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 15
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 15
#define CURLOPT_LOCALPORT 0
#define CURLOPT_LOCALPORTRANGE 0
#define CURLOPT_CONNECT_ONLY 0
#define CURLINFO_LASTSOCKET 0
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 15
#define CURLOPT_FTP_FILEMETHOD 0
#if (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 15
#define CURLOPT_FTP_SKIP_PASV_IP 0
#define CURLE_TFTP_NOTFOUND -1
#define CURLE_TFTP_PERM -1
#define CURLE_TFTP_DISKFULL -1
#define CURLE_TFTP_ILLEGAL -1
#define CURLE_TFTP_UNKNOWNID -1
#define CURLE_TFTP_EXISTS -1
#define CURLE_TFTP_NOSUCHUSER -1
#if (LIBCURL_VERSION_MINOR == 14 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 14
#define CURLOPT_COOKIELIST 0
#define CURLOPT_IGNORE_CONTENT_LENGTH 0
#define CURLINFO_COOKIELIST 0
#if (LIBCURL_VERSION_MINOR == 14 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 14
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 13
#define CURL_VERSION_SSPI 0
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 13
#define CURLE_LOGIN_DENIED -1
#if (LIBCURL_VERSION_MINOR == 13 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 13
#define CURLOPT_SOURCE_URL 0
#define CURLOPT_SOURCE_QUOTE 0
#define CURLOPT_FTP_ACCOUNT 0
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 12
#define CURLOPT_IOCTLFUNCTION 0
#define CURLOPT_IOCTLDATA 0
#define CURLE_SEND_FAIL_REWIND -1
#define CURLE_SSL_ENGINE_INITFAILED -1
#define CURLINFO_NUM_CONNECTS 0
#define CURLINFO_SSL_ENGINES 0
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 12
#define CURLOPT_FTPSSLAUTH 0
#define CURLINFO_OS_ERRNO 0
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 12
#define CURLOPT_SOURCE_HOST 0
#define CURLOPT_SOURCE_USERPWD 0
#define CURLOPT_SOURCE_PATH 0
#define CURLOPT_SOURCE_PORT 0
#define CURLOPT_PASV_HOST 0
#define CURLOPT_SOURCE_PREQUOTE 0
#define CURLOPT_SOURCE_POSTQUOTE 0
#if (LIBCURL_VERSION_MINOR == 12 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 12
#define CURLE_INTERFACE_FAILED -1
#define CURL_VERSION_IDN 0
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 11
#define CURLOPT_TCP_NODELAY 0
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 11
#define CURLOPT_POSTFIELDSIZE_LARGE 0
#define CURL_VERSION_LARGEFILE 0
#if (LIBCURL_VERSION_MINOR == 11 && LIBCURL_VERSION_PATCH < 0) || LIBCURL_VERSION_MINOR < 11
#define CURLOPT_INFILESIZE_LARGE 0
#define CURLOPT_RESUME_FROM_LARGE 0
#define CURLOPT_MAXFILESIZE_LARGE 0
#define CURLOPT_NETRC_FILE 0
#define CURLOPT_FTP_SSL 0
#define CURLE_FTP_SSL_FAILED -1
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 8) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_IPRESOLVE 0
#define CURLOPT_MAXFILESIZE 0
#define CURLE_LDAP_INVALID_URL -1
#define CURLE_FILESIZE_EXCEEDED -1
#define CURLINFO_RESPONSE_CODE 0
#define CURLINFO_HTTPAUTH_AVAIL 0
#define CURLINFO_PROXYAUTH_AVAIL 0
#define CURL_VERSION_SPNEGO 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 7) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_FTP_CREATE_MISSING_DIRS 0
#define CURLOPT_PROXYAUTH 0
#define CURLINFO_HTTP_CONNECTCODE 0
#define CURL_VERSION_ASYNCHDNS 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 6) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_HTTPAUTH 0
#define CURLOPT_SSL_CTX_FUNCTION 0
#define CURLOPT_SSL_CTX_DATA 0
#define CURL_VERSION_NTLM 0
#define CURL_VERSION_GSSNEGOTIATE 0
#define CURL_VERSION_DEBUG 0
#define CURLAUTH_NONE 0
#define CURLAUTH_BASIC 0
#define CURLAUTH_DIGEST 0
#define CURLAUTH_GSSNEGOTIATE 0
#define CURLAUTH_NTLM 0
#define CURLAUTH_ANY 0
#define CURLAUTH_ANYSAFE 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 5) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_FTP_USE_EPRT 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 4) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_UNRESTRICTED_AUTH 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 3) || LIBCURL_VERSION_MINOR < 10
#define CURLOPT_PRIVATE 0
#define CURLOPT_HTTP200ALIASES 0
#define CURLE_HTTP_RETURNED_ERROR -1
#define CURLINFO_PRIVATE 0
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 2) || LIBCURL_VERSION_MINOR < 10
#define CURLE_OPERATION_TIMEDOUT -1
#if (LIBCURL_VERSION_MINOR == 10 && LIBCURL_VERSION_PATCH < 1) || LIBCURL_VERSION_MINOR < 10
#error your version is TOOOOOOOO low
#endif /* 7.10.1 */
#endif /* 7.10.2 */
#endif /* 7.10.3 */
#endif /* 7.10.4 */
#endif /* 7.10.5 */
#endif /* 7.10.6 */
#endif /* 7.10.7 */
#endif /* 7.10.8 */
#endif /* 7.11.0 */
#endif /* 7.11.1 */
#endif /* 7.11.2 */
#endif /* 7.12.0 */
#endif /* 7.12.1 */
#endif /* 7.12.2 */
#endif /* 7.12.3 */
#endif /* 7.13.0 */
#endif /* 7.13.1 */
#endif /* 7.13.2 */
#endif /* 7.14.0 */
#endif /* 7.14.1 */
#endif /* 7.15.0 */
#endif /* 7.15.1 */
#endif /* 7.15.2 */
#endif /* 7.15.3 */
#endif /* 7.15.4 */
#endif /* 7.15.5 */
#endif /* 7.16.0 */
#endif /* 7.16.1 */
#endif /* 7.16.2 */
#endif /* 7.16.3 */
#endif /* 7.16.4 */
#endif /* 7.17.0 */
#endif /* 7.17.1 */
#endif /* 7.18.0 */
#endif /* 7.18.1 */
#endif /* 7.18.2 */
#endif /* 7.19.0 */
#endif /* 7.19.1 */
#endif /* 7.19.2 */
#endif /* 7.19.3 */
#endif /* 7.19.4 */
#endif /* 7.19.5 */
#endif /* 7.19.6 */
#endif /* 7.19.7 */
#endif /* 7.20.0 */
#endif /* 7.20.1 */
#endif /* 7.21.0 */
#endif /* 7.21.1 */
#endif /* 7.21.2 */
#endif /* 7.21.3 */
#endif /* 7.21.4 */
#endif /* 7.21.5 */
#endif /* 7.21.6 */
#endif /* 7.21.7 */
#endif /* 7.22.0 */
#endif /* 7.23.0 */
#endif /* 7.23.1 */
#endif /* 7.24.0 */
#endif /* 7.25.0 */
#endif /* 7.26.0 */
#endif /* 7.27.0 */
#endif /* 7.28.0 */
#endif /* 7.28.1 */
#endif /* 7.29.0 */
#endif /* 7.30.0 */
#endif /* 7.31.0 */
#endif /* 7.32.0 */
#endif /* 7.33.0 */
#endif /* 7.34.0 */
#endif /* 7.35.0 */
#endif /* 7.36.0 */
#endif /* 7.37.0 */
#endif /* 7.37.1 */
#endif /* 7.38.0 */
#endif /* 7.39.0 */
#endif /* 7.40.0 */
#endif /* 7.41.0 */
#endif /* 7.42.0 */
#endif /* 7.42.1 */
#endif /* 7.43.0 */
#endif /* 7.44.0 */
#endif /* 7.45.0 */
#endif /* 7.46.0 */
#endif /* 7.47.0 */
#endif /* 7.47.1 */
#endif /* 7.48.0 */
#endif /* 7.49.0 */
#endif /* 7.49.1 */
#endif /* 7.50.0 */
#endif /* 7.50.1 */
#endif /* 7.50.2 */
#endif /* 7.50.3 */
#endif /* 7.51.0 */
#endif /* 7.52.0 */
#endif /* 7.52.1 */
#endif /* 7.53.0 */
#endif /* 7.53.1 */
#endif /* 7.54.0 */
#endif /* 7.54.1 */
#endif /* 7.55.0 */
#endif /* 7.55.1 */
#endif /* 7.56.0 */
#endif /* 7.56.1 */
#endif /* 7.57.0 */
/* generated ends */
-179
View File
@@ -1,179 +0,0 @@
package curl
/*
#include <curl/curl.h>
#include "compat.h"
*/
import "C"
// for GlobalInit(flag)
const (
GLOBAL_SSL = C.CURL_GLOBAL_SSL
GLOBAL_WIN32 = C.CURL_GLOBAL_WIN32
GLOBAL_ALL = C.CURL_GLOBAL_ALL
GLOBAL_NOTHING = C.CURL_GLOBAL_NOTHING
GLOBAL_DEFAULT = C.CURL_GLOBAL_DEFAULT
)
// CURLMcode
const (
M_CALL_MULTI_PERFORM = C.CURLM_CALL_MULTI_PERFORM
// M_OK = C.CURLM_OK
M_BAD_HANDLE = C.CURLM_BAD_HANDLE
M_BAD_EASY_HANDLE = C.CURLM_BAD_EASY_HANDLE
M_OUT_OF_MEMORY = C.CURLM_OUT_OF_MEMORY
M_INTERNAL_ERROR = C.CURLM_INTERNAL_ERROR
M_BAD_SOCKET = C.CURLM_BAD_SOCKET
M_UNKNOWN_OPTION = C.CURLM_UNKNOWN_OPTION
)
// for multi.Setopt(flag, ...)
const (
MOPT_SOCKETFUNCTION = C.CURLMOPT_SOCKETFUNCTION
MOPT_SOCKETDATA = C.CURLMOPT_SOCKETDATA
MOPT_PIPELINING = C.CURLMOPT_PIPELINING
MOPT_TIMERFUNCTION = C.CURLMOPT_TIMERFUNCTION
MOPT_TIMERDATA = C.CURLMOPT_TIMERDATA
MOPT_MAXCONNECTS = C.CURLMOPT_MAXCONNECTS
)
// CURLSHcode
const (
// SHE_OK = C.CURLSHE_OK
SHE_BAD_OPTION = C.CURLSHE_BAD_OPTION
SHE_IN_USE = C.CURLSHE_IN_USE
SHE_INVALID = C.CURLSHE_INVALID
SHE_NOMEM = C.CURLSHE_NOMEM
)
// for share.Setopt(flat, ...)
const (
SHOPT_SHARE = C.CURLSHOPT_SHARE
SHOPT_UNSHARE = C.CURLSHOPT_UNSHARE
SHOPT_LOCKFUNC = C.CURLSHOPT_LOCKFUNC
SHOPT_UNLOCKFUNC = C.CURLSHOPT_UNLOCKFUNC
SHOPT_USERDATA = C.CURLSHOPT_USERDATA
)
// for share.Setopt(SHOPT_SHARE/SHOPT_UNSHARE, flag)
const (
LOCK_DATA_SHARE = C.CURL_LOCK_DATA_SHARE
LOCK_DATA_COOKIE = C.CURL_LOCK_DATA_COOKIE
LOCK_DATA_DNS = C.CURL_LOCK_DATA_DNS
LOCK_DATA_SSL_SESSION = C.CURL_LOCK_DATA_SSL_SESSION
LOCK_DATA_CONNECT = C.CURL_LOCK_DATA_CONNECT
)
// for VersionInfo(flag)
const (
VERSION_FIRST = C.CURLVERSION_FIRST
VERSION_SECOND = C.CURLVERSION_SECOND
VERSION_THIRD = C.CURLVERSION_THIRD
// VERSION_FOURTH = C.CURLVERSION_FOURTH
VERSION_LAST = C.CURLVERSION_LAST
VERSION_NOW = C.CURLVERSION_NOW
)
// for VersionInfo(...).Features mask flag
const (
VERSION_IPV6 = C.CURL_VERSION_IPV6
VERSION_KERBEROS4 = C.CURL_VERSION_KERBEROS4
VERSION_SSL = C.CURL_VERSION_SSL
VERSION_LIBZ = C.CURL_VERSION_LIBZ
VERSION_NTLM = C.CURL_VERSION_NTLM
VERSION_GSSNEGOTIATE = C.CURL_VERSION_GSSNEGOTIATE
VERSION_DEBUG = C.CURL_VERSION_DEBUG
VERSION_ASYNCHDNS = C.CURL_VERSION_ASYNCHDNS
VERSION_SPNEGO = C.CURL_VERSION_SPNEGO
VERSION_LARGEFILE = C.CURL_VERSION_LARGEFILE
VERSION_IDN = C.CURL_VERSION_IDN
VERSION_SSPI = C.CURL_VERSION_SSPI
VERSION_CONV = C.CURL_VERSION_CONV
VERSION_CURLDEBUG = C.CURL_VERSION_CURLDEBUG
VERSION_TLSAUTH_SRP = C.CURL_VERSION_TLSAUTH_SRP
VERSION_NTLM_WB = C.CURL_VERSION_NTLM_WB
)
// for OPT_READFUNCTION, return a int flag
const (
READFUNC_ABORT = C.CURL_READFUNC_ABORT
READFUNC_PAUSE = C.CURL_READFUNC_PAUSE
)
// for easy.Setopt(OPT_HTTP_VERSION, flag)
const (
HTTP_VERSION_NONE = C.CURL_HTTP_VERSION_NONE
HTTP_VERSION_1_0 = C.CURL_HTTP_VERSION_1_0
HTTP_VERSION_1_1 = C.CURL_HTTP_VERSION_1_1
)
// for easy.Setopt(OPT_PROXYTYPE, flag)
const (
PROXY_HTTP = C.CURLPROXY_HTTP /* added in 7.10, new in 7.19.4 default is to use CONNECT HTTP/1.1 */
PROXY_HTTP_1_0 = C.CURLPROXY_HTTP_1_0 /* added in 7.19.4, force to use CONNECT HTTP/1.0 */
PROXY_SOCKS4 = C.CURLPROXY_SOCKS4 /* support added in 7.15.2, enum existed already in 7.10 */
PROXY_SOCKS5 = C.CURLPROXY_SOCKS5 /* added in 7.10 */
PROXY_SOCKS4A = C.CURLPROXY_SOCKS4A /* added in 7.18.0 */
// Use the SOCKS5 protocol but pass along the host name rather than the IP address.
PROXY_SOCKS5_HOSTNAME = C.CURLPROXY_SOCKS5_HOSTNAME
)
// for easy.Setopt(OPT_SSLVERSION, flag)
const (
SSLVERSION_DEFAULT = C.CURL_SSLVERSION_DEFAULT
SSLVERSION_TLSv1 = C.CURL_SSLVERSION_TLSv1
SSLVERSION_SSLv2 = C.CURL_SSLVERSION_SSLv2
SSLVERSION_SSLv3 = C.CURL_SSLVERSION_SSLv3
)
// for easy.Setopt(OPT_TIMECONDITION, flag)
const (
TIMECOND_NONE = C.CURL_TIMECOND_NONE
TIMECOND_IFMODSINCE = C.CURL_TIMECOND_IFMODSINCE
TIMECOND_IFUNMODSINCE = C.CURL_TIMECOND_IFUNMODSINCE
TIMECOND_LASTMOD = C.CURL_TIMECOND_LASTMOD
)
// for easy.Setopt(OPT_NETRC, flag)
const (
NETRC_IGNORED = C.CURL_NETRC_IGNORED
NETRC_OPTIONAL = C.CURL_NETRC_OPTIONAL
NETRC_REQUIRED = C.CURL_NETRC_REQUIRED
)
// for easy.Setopt(OPT_FTP_CREATE_MISSING_DIRS, flag)
const (
FTP_CREATE_DIR_NONE = C.CURLFTP_CREATE_DIR_NONE
FTP_CREATE_DIR = C.CURLFTP_CREATE_DIR
FTP_CREATE_DIR_RETRY = C.CURLFTP_CREATE_DIR_RETRY
)
// for easy.Setopt(OPT_IPRESOLVE, flag)
const (
IPRESOLVE_WHATEVER = C.CURL_IPRESOLVE_WHATEVER
IPRESOLVE_V4 = C.CURL_IPRESOLVE_V4
IPRESOLVE_V6 = C.CURL_IPRESOLVE_V6
)
// for easy.Setopt(OPT_SSL_OPTIONS, flag)
const (
SSLOPT_ALLOW_BEAST = 1
)
// for easy.Pause(flat)
const (
PAUSE_RECV = C.CURLPAUSE_RECV
PAUSE_RECV_CONT = C.CURLPAUSE_RECV_CONT
PAUSE_SEND = C.CURLPAUSE_SEND
PAUSE_SEND_CONT = C.CURLPAUSE_SEND_CONT
PAUSE_ALL = C.CURLPAUSE_ALL
PAUSE_CONT = C.CURLPAUSE_CONT
)
// for multi.Info_read()
const (
CURLMSG_NONE = C.CURLMSG_NONE
CURLMSG_DONE = C.CURLMSG_DONE
CURLMSG_LAST = C.CURLMSG_LAST
)
-488
View File
@@ -1,488 +0,0 @@
//go:generate /usr/bin/env python ./misc/codegen.py
package curl
/*
#include <curl/curl.h>
#include "compat.h"
*/
import "C"
// CURLcode
const (
E_UNSUPPORTED_PROTOCOL = C.CURLE_UNSUPPORTED_PROTOCOL
E_FAILED_INIT = C.CURLE_FAILED_INIT
E_URL_MALFORMAT = C.CURLE_URL_MALFORMAT
E_NOT_BUILT_IN = C.CURLE_NOT_BUILT_IN
E_COULDNT_RESOLVE_PROXY = C.CURLE_COULDNT_RESOLVE_PROXY
E_COULDNT_RESOLVE_HOST = C.CURLE_COULDNT_RESOLVE_HOST
E_COULDNT_CONNECT = C.CURLE_COULDNT_CONNECT
E_WEIRD_SERVER_REPLY = C.CURLE_WEIRD_SERVER_REPLY
E_REMOTE_ACCESS_DENIED = C.CURLE_REMOTE_ACCESS_DENIED
E_FTP_ACCEPT_FAILED = C.CURLE_FTP_ACCEPT_FAILED
E_FTP_WEIRD_PASS_REPLY = C.CURLE_FTP_WEIRD_PASS_REPLY
E_FTP_ACCEPT_TIMEOUT = C.CURLE_FTP_ACCEPT_TIMEOUT
E_FTP_WEIRD_PASV_REPLY = C.CURLE_FTP_WEIRD_PASV_REPLY
E_FTP_WEIRD_227_FORMAT = C.CURLE_FTP_WEIRD_227_FORMAT
E_FTP_CANT_GET_HOST = C.CURLE_FTP_CANT_GET_HOST
E_HTTP2 = C.CURLE_HTTP2
E_FTP_COULDNT_SET_TYPE = C.CURLE_FTP_COULDNT_SET_TYPE
E_PARTIAL_FILE = C.CURLE_PARTIAL_FILE
E_FTP_COULDNT_RETR_FILE = C.CURLE_FTP_COULDNT_RETR_FILE
E_OBSOLETE20 = C.CURLE_OBSOLETE20
E_QUOTE_ERROR = C.CURLE_QUOTE_ERROR
E_HTTP_RETURNED_ERROR = C.CURLE_HTTP_RETURNED_ERROR
E_WRITE_ERROR = C.CURLE_WRITE_ERROR
E_OBSOLETE24 = C.CURLE_OBSOLETE24
E_UPLOAD_FAILED = C.CURLE_UPLOAD_FAILED
E_READ_ERROR = C.CURLE_READ_ERROR
E_OUT_OF_MEMORY = C.CURLE_OUT_OF_MEMORY
E_OPERATION_TIMEDOUT = C.CURLE_OPERATION_TIMEDOUT
E_OBSOLETE29 = C.CURLE_OBSOLETE29
E_FTP_PORT_FAILED = C.CURLE_FTP_PORT_FAILED
E_FTP_COULDNT_USE_REST = C.CURLE_FTP_COULDNT_USE_REST
E_OBSOLETE32 = C.CURLE_OBSOLETE32
E_RANGE_ERROR = C.CURLE_RANGE_ERROR
E_HTTP_POST_ERROR = C.CURLE_HTTP_POST_ERROR
E_SSL_CONNECT_ERROR = C.CURLE_SSL_CONNECT_ERROR
E_BAD_DOWNLOAD_RESUME = C.CURLE_BAD_DOWNLOAD_RESUME
E_FILE_COULDNT_READ_FILE = C.CURLE_FILE_COULDNT_READ_FILE
E_LDAP_CANNOT_BIND = C.CURLE_LDAP_CANNOT_BIND
E_LDAP_SEARCH_FAILED = C.CURLE_LDAP_SEARCH_FAILED
E_OBSOLETE40 = C.CURLE_OBSOLETE40
E_FUNCTION_NOT_FOUND = C.CURLE_FUNCTION_NOT_FOUND
E_ABORTED_BY_CALLBACK = C.CURLE_ABORTED_BY_CALLBACK
E_BAD_FUNCTION_ARGUMENT = C.CURLE_BAD_FUNCTION_ARGUMENT
E_OBSOLETE44 = C.CURLE_OBSOLETE44
E_INTERFACE_FAILED = C.CURLE_INTERFACE_FAILED
E_OBSOLETE46 = C.CURLE_OBSOLETE46
E_TOO_MANY_REDIRECTS = C.CURLE_TOO_MANY_REDIRECTS
E_UNKNOWN_OPTION = C.CURLE_UNKNOWN_OPTION
E_TELNET_OPTION_SYNTAX = C.CURLE_TELNET_OPTION_SYNTAX
E_OBSOLETE50 = C.CURLE_OBSOLETE50
E_PEER_FAILED_VERIFICATION = C.CURLE_PEER_FAILED_VERIFICATION
E_GOT_NOTHING = C.CURLE_GOT_NOTHING
E_SSL_ENGINE_NOTFOUND = C.CURLE_SSL_ENGINE_NOTFOUND
E_SSL_ENGINE_SETFAILED = C.CURLE_SSL_ENGINE_SETFAILED
E_SEND_ERROR = C.CURLE_SEND_ERROR
E_RECV_ERROR = C.CURLE_RECV_ERROR
E_OBSOLETE57 = C.CURLE_OBSOLETE57
E_SSL_CERTPROBLEM = C.CURLE_SSL_CERTPROBLEM
E_SSL_CIPHER = C.CURLE_SSL_CIPHER
E_SSL_CACERT = C.CURLE_SSL_CACERT
E_BAD_CONTENT_ENCODING = C.CURLE_BAD_CONTENT_ENCODING
E_LDAP_INVALID_URL = C.CURLE_LDAP_INVALID_URL
E_FILESIZE_EXCEEDED = C.CURLE_FILESIZE_EXCEEDED
E_USE_SSL_FAILED = C.CURLE_USE_SSL_FAILED
E_SEND_FAIL_REWIND = C.CURLE_SEND_FAIL_REWIND
E_SSL_ENGINE_INITFAILED = C.CURLE_SSL_ENGINE_INITFAILED
E_LOGIN_DENIED = C.CURLE_LOGIN_DENIED
E_TFTP_NOTFOUND = C.CURLE_TFTP_NOTFOUND
E_TFTP_PERM = C.CURLE_TFTP_PERM
E_REMOTE_DISK_FULL = C.CURLE_REMOTE_DISK_FULL
E_TFTP_ILLEGAL = C.CURLE_TFTP_ILLEGAL
E_TFTP_UNKNOWNID = C.CURLE_TFTP_UNKNOWNID
E_REMOTE_FILE_EXISTS = C.CURLE_REMOTE_FILE_EXISTS
E_TFTP_NOSUCHUSER = C.CURLE_TFTP_NOSUCHUSER
E_CONV_FAILED = C.CURLE_CONV_FAILED
E_CONV_REQD = C.CURLE_CONV_REQD
E_SSL_CACERT_BADFILE = C.CURLE_SSL_CACERT_BADFILE
E_REMOTE_FILE_NOT_FOUND = C.CURLE_REMOTE_FILE_NOT_FOUND
E_SSH = C.CURLE_SSH
E_SSL_SHUTDOWN_FAILED = C.CURLE_SSL_SHUTDOWN_FAILED
E_AGAIN = C.CURLE_AGAIN
E_SSL_CRL_BADFILE = C.CURLE_SSL_CRL_BADFILE
E_SSL_ISSUER_ERROR = C.CURLE_SSL_ISSUER_ERROR
E_FTP_PRET_FAILED = C.CURLE_FTP_PRET_FAILED
E_RTSP_CSEQ_ERROR = C.CURLE_RTSP_CSEQ_ERROR
E_RTSP_SESSION_ERROR = C.CURLE_RTSP_SESSION_ERROR
E_FTP_BAD_FILE_LIST = C.CURLE_FTP_BAD_FILE_LIST
E_CHUNK_FAILED = C.CURLE_CHUNK_FAILED
E_NO_CONNECTION_AVAILABLE = C.CURLE_NO_CONNECTION_AVAILABLE
E_SSL_PINNEDPUBKEYNOTMATCH = C.CURLE_SSL_PINNEDPUBKEYNOTMATCH
E_SSL_INVALIDCERTSTATUS = C.CURLE_SSL_INVALIDCERTSTATUS
E_HTTP2_STREAM = C.CURLE_HTTP2_STREAM
E_OBSOLETE16 = C.CURLE_OBSOLETE16
E_OBSOLETE10 = C.CURLE_OBSOLETE10
E_OBSOLETE12 = C.CURLE_OBSOLETE12
E_FTP_WEIRD_SERVER_REPLY = C.CURLE_FTP_WEIRD_SERVER_REPLY
E_UNKNOWN_TELNET_OPTION = C.CURLE_UNKNOWN_TELNET_OPTION
E_SSL_PEER_CERTIFICATE = C.CURLE_SSL_PEER_CERTIFICATE
E_OBSOLETE = C.CURLE_OBSOLETE
E_BAD_PASSWORD_ENTERED = C.CURLE_BAD_PASSWORD_ENTERED
E_BAD_CALLING_ORDER = C.CURLE_BAD_CALLING_ORDER
E_FTP_USER_PASSWORD_INCORRECT = C.CURLE_FTP_USER_PASSWORD_INCORRECT
E_FTP_CANT_RECONNECT = C.CURLE_FTP_CANT_RECONNECT
E_FTP_COULDNT_GET_SIZE = C.CURLE_FTP_COULDNT_GET_SIZE
E_FTP_COULDNT_SET_ASCII = C.CURLE_FTP_COULDNT_SET_ASCII
E_FTP_WEIRD_USER_REPLY = C.CURLE_FTP_WEIRD_USER_REPLY
E_FTP_WRITE_ERROR = C.CURLE_FTP_WRITE_ERROR
E_LIBRARY_NOT_FOUND = C.CURLE_LIBRARY_NOT_FOUND
E_MALFORMAT_USER = C.CURLE_MALFORMAT_USER
E_SHARE_IN_USE = C.CURLE_SHARE_IN_USE
E_URL_MALFORMAT_USER = C.CURLE_URL_MALFORMAT_USER
E_FTP_ACCESS_DENIED = C.CURLE_FTP_ACCESS_DENIED
E_FTP_COULDNT_SET_BINARY = C.CURLE_FTP_COULDNT_SET_BINARY
E_FTP_QUOTE_ERROR = C.CURLE_FTP_QUOTE_ERROR
E_TFTP_DISKFULL = C.CURLE_TFTP_DISKFULL
E_TFTP_EXISTS = C.CURLE_TFTP_EXISTS
E_HTTP_RANGE_ERROR = C.CURLE_HTTP_RANGE_ERROR
E_FTP_SSL_FAILED = C.CURLE_FTP_SSL_FAILED
E_OPERATION_TIMEOUTED = C.CURLE_OPERATION_TIMEOUTED
E_HTTP_NOT_FOUND = C.CURLE_HTTP_NOT_FOUND
E_HTTP_PORT_FAILED = C.CURLE_HTTP_PORT_FAILED
E_FTP_COULDNT_STOR_FILE = C.CURLE_FTP_COULDNT_STOR_FILE
E_FTP_PARTIAL_FILE = C.CURLE_FTP_PARTIAL_FILE
E_FTP_BAD_DOWNLOAD_RESUME = C.CURLE_FTP_BAD_DOWNLOAD_RESUME
E_ALREADY_COMPLETE = C.CURLE_ALREADY_COMPLETE
)
// easy.Setopt(flag, ...)
const (
OPT_ENCODING = C.CURLOPT_ENCODING
OPT_FILE = C.CURLOPT_FILE
OPT_INFILE = C.CURLOPT_INFILE
OPT_WRITEHEADER = C.CURLOPT_WRITEHEADER
OPT_WRITEINFO = C.CURLOPT_WRITEINFO
OPT_CLOSEPOLICY = C.CURLOPT_CLOSEPOLICY
OPT_WRITEDATA = C.CURLOPT_WRITEDATA
OPT_URL = C.CURLOPT_URL
OPT_PORT = C.CURLOPT_PORT
OPT_PROXY = C.CURLOPT_PROXY
OPT_USERPWD = C.CURLOPT_USERPWD
OPT_PROXYUSERPWD = C.CURLOPT_PROXYUSERPWD
OPT_RANGE = C.CURLOPT_RANGE
OPT_READDATA = C.CURLOPT_READDATA
OPT_ERRORBUFFER = C.CURLOPT_ERRORBUFFER
OPT_WRITEFUNCTION = C.CURLOPT_WRITEFUNCTION
OPT_READFUNCTION = C.CURLOPT_READFUNCTION
OPT_TIMEOUT = C.CURLOPT_TIMEOUT
OPT_INFILESIZE = C.CURLOPT_INFILESIZE
OPT_POSTFIELDS = C.CURLOPT_POSTFIELDS
OPT_REFERER = C.CURLOPT_REFERER
OPT_FTPPORT = C.CURLOPT_FTPPORT
OPT_USERAGENT = C.CURLOPT_USERAGENT
OPT_LOW_SPEED_LIMIT = C.CURLOPT_LOW_SPEED_LIMIT
OPT_LOW_SPEED_TIME = C.CURLOPT_LOW_SPEED_TIME
OPT_RESUME_FROM = C.CURLOPT_RESUME_FROM
OPT_COOKIE = C.CURLOPT_COOKIE
OPT_HTTPHEADER = C.CURLOPT_HTTPHEADER
OPT_HTTPPOST = C.CURLOPT_HTTPPOST
OPT_SSLCERT = C.CURLOPT_SSLCERT
OPT_KEYPASSWD = C.CURLOPT_KEYPASSWD
OPT_CRLF = C.CURLOPT_CRLF
OPT_QUOTE = C.CURLOPT_QUOTE
OPT_HEADERDATA = C.CURLOPT_HEADERDATA
OPT_COOKIEFILE = C.CURLOPT_COOKIEFILE
OPT_SSLVERSION = C.CURLOPT_SSLVERSION
OPT_TIMECONDITION = C.CURLOPT_TIMECONDITION
OPT_TIMEVALUE = C.CURLOPT_TIMEVALUE
OPT_CUSTOMREQUEST = C.CURLOPT_CUSTOMREQUEST
OPT_STDERR = C.CURLOPT_STDERR
OPT_POSTQUOTE = C.CURLOPT_POSTQUOTE
OPT_OBSOLETE40 = C.CURLOPT_OBSOLETE40
OPT_VERBOSE = C.CURLOPT_VERBOSE
OPT_HEADER = C.CURLOPT_HEADER
OPT_NOPROGRESS = C.CURLOPT_NOPROGRESS
OPT_NOBODY = C.CURLOPT_NOBODY
OPT_FAILONERROR = C.CURLOPT_FAILONERROR
OPT_UPLOAD = C.CURLOPT_UPLOAD
OPT_POST = C.CURLOPT_POST
OPT_DIRLISTONLY = C.CURLOPT_DIRLISTONLY
OPT_APPEND = C.CURLOPT_APPEND
OPT_NETRC = C.CURLOPT_NETRC
OPT_FOLLOWLOCATION = C.CURLOPT_FOLLOWLOCATION
OPT_TRANSFERTEXT = C.CURLOPT_TRANSFERTEXT
OPT_PUT = C.CURLOPT_PUT
OPT_PROGRESSFUNCTION = C.CURLOPT_PROGRESSFUNCTION
OPT_PROGRESSDATA = C.CURLOPT_PROGRESSDATA
OPT_XFERINFODATA = C.CURLOPT_XFERINFODATA
OPT_AUTOREFERER = C.CURLOPT_AUTOREFERER
OPT_PROXYPORT = C.CURLOPT_PROXYPORT
OPT_POSTFIELDSIZE = C.CURLOPT_POSTFIELDSIZE
OPT_HTTPPROXYTUNNEL = C.CURLOPT_HTTPPROXYTUNNEL
OPT_INTERFACE = C.CURLOPT_INTERFACE
OPT_KRBLEVEL = C.CURLOPT_KRBLEVEL
OPT_SSL_VERIFYPEER = C.CURLOPT_SSL_VERIFYPEER
OPT_CAINFO = C.CURLOPT_CAINFO
OPT_MAXREDIRS = C.CURLOPT_MAXREDIRS
OPT_FILETIME = C.CURLOPT_FILETIME
OPT_TELNETOPTIONS = C.CURLOPT_TELNETOPTIONS
OPT_MAXCONNECTS = C.CURLOPT_MAXCONNECTS
OPT_OBSOLETE72 = C.CURLOPT_OBSOLETE72
OPT_FRESH_CONNECT = C.CURLOPT_FRESH_CONNECT
OPT_FORBID_REUSE = C.CURLOPT_FORBID_REUSE
OPT_RANDOM_FILE = C.CURLOPT_RANDOM_FILE
OPT_EGDSOCKET = C.CURLOPT_EGDSOCKET
OPT_CONNECTTIMEOUT = C.CURLOPT_CONNECTTIMEOUT
OPT_HEADERFUNCTION = C.CURLOPT_HEADERFUNCTION
OPT_HTTPGET = C.CURLOPT_HTTPGET
OPT_SSL_VERIFYHOST = C.CURLOPT_SSL_VERIFYHOST
OPT_COOKIEJAR = C.CURLOPT_COOKIEJAR
OPT_SSL_CIPHER_LIST = C.CURLOPT_SSL_CIPHER_LIST
OPT_HTTP_VERSION = C.CURLOPT_HTTP_VERSION
OPT_FTP_USE_EPSV = C.CURLOPT_FTP_USE_EPSV
OPT_SSLCERTTYPE = C.CURLOPT_SSLCERTTYPE
OPT_SSLKEY = C.CURLOPT_SSLKEY
OPT_SSLKEYTYPE = C.CURLOPT_SSLKEYTYPE
OPT_SSLENGINE = C.CURLOPT_SSLENGINE
OPT_SSLENGINE_DEFAULT = C.CURLOPT_SSLENGINE_DEFAULT
OPT_DNS_USE_GLOBAL_CACHE = C.CURLOPT_DNS_USE_GLOBAL_CACHE
OPT_DNS_CACHE_TIMEOUT = C.CURLOPT_DNS_CACHE_TIMEOUT
OPT_PREQUOTE = C.CURLOPT_PREQUOTE
OPT_DEBUGFUNCTION = C.CURLOPT_DEBUGFUNCTION
OPT_DEBUGDATA = C.CURLOPT_DEBUGDATA
OPT_COOKIESESSION = C.CURLOPT_COOKIESESSION
OPT_CAPATH = C.CURLOPT_CAPATH
OPT_BUFFERSIZE = C.CURLOPT_BUFFERSIZE
OPT_NOSIGNAL = C.CURLOPT_NOSIGNAL
OPT_SHARE = C.CURLOPT_SHARE
OPT_PROXYTYPE = C.CURLOPT_PROXYTYPE
OPT_ACCEPT_ENCODING = C.CURLOPT_ACCEPT_ENCODING
OPT_PRIVATE = C.CURLOPT_PRIVATE
OPT_HTTP200ALIASES = C.CURLOPT_HTTP200ALIASES
OPT_UNRESTRICTED_AUTH = C.CURLOPT_UNRESTRICTED_AUTH
OPT_FTP_USE_EPRT = C.CURLOPT_FTP_USE_EPRT
OPT_HTTPAUTH = C.CURLOPT_HTTPAUTH
OPT_SSL_CTX_FUNCTION = C.CURLOPT_SSL_CTX_FUNCTION
OPT_SSL_CTX_DATA = C.CURLOPT_SSL_CTX_DATA
OPT_FTP_CREATE_MISSING_DIRS = C.CURLOPT_FTP_CREATE_MISSING_DIRS
OPT_PROXYAUTH = C.CURLOPT_PROXYAUTH
OPT_FTP_RESPONSE_TIMEOUT = C.CURLOPT_FTP_RESPONSE_TIMEOUT
OPT_SERVER_RESPONSE_TIMEOUT = C.CURLOPT_SERVER_RESPONSE_TIMEOUT
OPT_IPRESOLVE = C.CURLOPT_IPRESOLVE
OPT_MAXFILESIZE = C.CURLOPT_MAXFILESIZE
OPT_INFILESIZE_LARGE = C.CURLOPT_INFILESIZE_LARGE
OPT_RESUME_FROM_LARGE = C.CURLOPT_RESUME_FROM_LARGE
OPT_MAXFILESIZE_LARGE = C.CURLOPT_MAXFILESIZE_LARGE
OPT_NETRC_FILE = C.CURLOPT_NETRC_FILE
OPT_USE_SSL = C.CURLOPT_USE_SSL
OPT_POSTFIELDSIZE_LARGE = C.CURLOPT_POSTFIELDSIZE_LARGE
OPT_TCP_NODELAY = C.CURLOPT_TCP_NODELAY
OPT_FTPSSLAUTH = C.CURLOPT_FTPSSLAUTH
OPT_IOCTLFUNCTION = C.CURLOPT_IOCTLFUNCTION
OPT_IOCTLDATA = C.CURLOPT_IOCTLDATA
OPT_FTP_ACCOUNT = C.CURLOPT_FTP_ACCOUNT
OPT_COOKIELIST = C.CURLOPT_COOKIELIST
OPT_IGNORE_CONTENT_LENGTH = C.CURLOPT_IGNORE_CONTENT_LENGTH
OPT_FTP_SKIP_PASV_IP = C.CURLOPT_FTP_SKIP_PASV_IP
OPT_FTP_FILEMETHOD = C.CURLOPT_FTP_FILEMETHOD
OPT_LOCALPORT = C.CURLOPT_LOCALPORT
OPT_LOCALPORTRANGE = C.CURLOPT_LOCALPORTRANGE
OPT_CONNECT_ONLY = C.CURLOPT_CONNECT_ONLY
OPT_CONV_FROM_NETWORK_FUNCTION = C.CURLOPT_CONV_FROM_NETWORK_FUNCTION
OPT_CONV_TO_NETWORK_FUNCTION = C.CURLOPT_CONV_TO_NETWORK_FUNCTION
OPT_CONV_FROM_UTF8_FUNCTION = C.CURLOPT_CONV_FROM_UTF8_FUNCTION
OPT_MAX_SEND_SPEED_LARGE = C.CURLOPT_MAX_SEND_SPEED_LARGE
OPT_MAX_RECV_SPEED_LARGE = C.CURLOPT_MAX_RECV_SPEED_LARGE
OPT_FTP_ALTERNATIVE_TO_USER = C.CURLOPT_FTP_ALTERNATIVE_TO_USER
OPT_SOCKOPTFUNCTION = C.CURLOPT_SOCKOPTFUNCTION
OPT_SOCKOPTDATA = C.CURLOPT_SOCKOPTDATA
OPT_SSL_SESSIONID_CACHE = C.CURLOPT_SSL_SESSIONID_CACHE
OPT_SSH_AUTH_TYPES = C.CURLOPT_SSH_AUTH_TYPES
OPT_SSH_PUBLIC_KEYFILE = C.CURLOPT_SSH_PUBLIC_KEYFILE
OPT_SSH_PRIVATE_KEYFILE = C.CURLOPT_SSH_PRIVATE_KEYFILE
OPT_FTP_SSL_CCC = C.CURLOPT_FTP_SSL_CCC
OPT_TIMEOUT_MS = C.CURLOPT_TIMEOUT_MS
OPT_CONNECTTIMEOUT_MS = C.CURLOPT_CONNECTTIMEOUT_MS
OPT_HTTP_TRANSFER_DECODING = C.CURLOPT_HTTP_TRANSFER_DECODING
OPT_HTTP_CONTENT_DECODING = C.CURLOPT_HTTP_CONTENT_DECODING
OPT_NEW_FILE_PERMS = C.CURLOPT_NEW_FILE_PERMS
OPT_NEW_DIRECTORY_PERMS = C.CURLOPT_NEW_DIRECTORY_PERMS
OPT_POSTREDIR = C.CURLOPT_POSTREDIR
OPT_SSH_HOST_PUBLIC_KEY_MD5 = C.CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
OPT_OPENSOCKETFUNCTION = C.CURLOPT_OPENSOCKETFUNCTION
OPT_OPENSOCKETDATA = C.CURLOPT_OPENSOCKETDATA
OPT_COPYPOSTFIELDS = C.CURLOPT_COPYPOSTFIELDS
OPT_PROXY_TRANSFER_MODE = C.CURLOPT_PROXY_TRANSFER_MODE
OPT_SEEKFUNCTION = C.CURLOPT_SEEKFUNCTION
OPT_SEEKDATA = C.CURLOPT_SEEKDATA
OPT_CRLFILE = C.CURLOPT_CRLFILE
OPT_ISSUERCERT = C.CURLOPT_ISSUERCERT
OPT_ADDRESS_SCOPE = C.CURLOPT_ADDRESS_SCOPE
OPT_CERTINFO = C.CURLOPT_CERTINFO
OPT_USERNAME = C.CURLOPT_USERNAME
OPT_PASSWORD = C.CURLOPT_PASSWORD
OPT_PROXYUSERNAME = C.CURLOPT_PROXYUSERNAME
OPT_PROXYPASSWORD = C.CURLOPT_PROXYPASSWORD
OPT_NOPROXY = C.CURLOPT_NOPROXY
OPT_TFTP_BLKSIZE = C.CURLOPT_TFTP_BLKSIZE
OPT_SOCKS5_GSSAPI_SERVICE = C.CURLOPT_SOCKS5_GSSAPI_SERVICE
OPT_SOCKS5_GSSAPI_NEC = C.CURLOPT_SOCKS5_GSSAPI_NEC
OPT_PROTOCOLS = C.CURLOPT_PROTOCOLS
OPT_REDIR_PROTOCOLS = C.CURLOPT_REDIR_PROTOCOLS
OPT_SSH_KNOWNHOSTS = C.CURLOPT_SSH_KNOWNHOSTS
OPT_SSH_KEYFUNCTION = C.CURLOPT_SSH_KEYFUNCTION
OPT_SSH_KEYDATA = C.CURLOPT_SSH_KEYDATA
OPT_MAIL_FROM = C.CURLOPT_MAIL_FROM
OPT_MAIL_RCPT = C.CURLOPT_MAIL_RCPT
OPT_FTP_USE_PRET = C.CURLOPT_FTP_USE_PRET
OPT_RTSP_REQUEST = C.CURLOPT_RTSP_REQUEST
OPT_RTSP_SESSION_ID = C.CURLOPT_RTSP_SESSION_ID
OPT_RTSP_STREAM_URI = C.CURLOPT_RTSP_STREAM_URI
OPT_RTSP_TRANSPORT = C.CURLOPT_RTSP_TRANSPORT
OPT_RTSP_CLIENT_CSEQ = C.CURLOPT_RTSP_CLIENT_CSEQ
OPT_RTSP_SERVER_CSEQ = C.CURLOPT_RTSP_SERVER_CSEQ
OPT_INTERLEAVEDATA = C.CURLOPT_INTERLEAVEDATA
OPT_INTERLEAVEFUNCTION = C.CURLOPT_INTERLEAVEFUNCTION
OPT_WILDCARDMATCH = C.CURLOPT_WILDCARDMATCH
OPT_CHUNK_BGN_FUNCTION = C.CURLOPT_CHUNK_BGN_FUNCTION
OPT_CHUNK_END_FUNCTION = C.CURLOPT_CHUNK_END_FUNCTION
OPT_FNMATCH_FUNCTION = C.CURLOPT_FNMATCH_FUNCTION
OPT_CHUNK_DATA = C.CURLOPT_CHUNK_DATA
OPT_FNMATCH_DATA = C.CURLOPT_FNMATCH_DATA
OPT_RESOLVE = C.CURLOPT_RESOLVE
OPT_TLSAUTH_USERNAME = C.CURLOPT_TLSAUTH_USERNAME
OPT_TLSAUTH_PASSWORD = C.CURLOPT_TLSAUTH_PASSWORD
OPT_TLSAUTH_TYPE = C.CURLOPT_TLSAUTH_TYPE
OPT_TRANSFER_ENCODING = C.CURLOPT_TRANSFER_ENCODING
OPT_CLOSESOCKETFUNCTION = C.CURLOPT_CLOSESOCKETFUNCTION
OPT_CLOSESOCKETDATA = C.CURLOPT_CLOSESOCKETDATA
OPT_GSSAPI_DELEGATION = C.CURLOPT_GSSAPI_DELEGATION
OPT_DNS_SERVERS = C.CURLOPT_DNS_SERVERS
OPT_ACCEPTTIMEOUT_MS = C.CURLOPT_ACCEPTTIMEOUT_MS
OPT_TCP_KEEPALIVE = C.CURLOPT_TCP_KEEPALIVE
OPT_TCP_KEEPIDLE = C.CURLOPT_TCP_KEEPIDLE
OPT_TCP_KEEPINTVL = C.CURLOPT_TCP_KEEPINTVL
OPT_SSL_OPTIONS = C.CURLOPT_SSL_OPTIONS
OPT_MAIL_AUTH = C.CURLOPT_MAIL_AUTH
OPT_SASL_IR = C.CURLOPT_SASL_IR
OPT_XFERINFOFUNCTION = C.CURLOPT_XFERINFOFUNCTION
OPT_XOAUTH2_BEARER = C.CURLOPT_XOAUTH2_BEARER
OPT_DNS_INTERFACE = C.CURLOPT_DNS_INTERFACE
OPT_DNS_LOCAL_IP4 = C.CURLOPT_DNS_LOCAL_IP4
OPT_DNS_LOCAL_IP6 = C.CURLOPT_DNS_LOCAL_IP6
OPT_LOGIN_OPTIONS = C.CURLOPT_LOGIN_OPTIONS
OPT_SSL_ENABLE_NPN = C.CURLOPT_SSL_ENABLE_NPN
OPT_SSL_ENABLE_ALPN = C.CURLOPT_SSL_ENABLE_ALPN
OPT_EXPECT_100_TIMEOUT_MS = C.CURLOPT_EXPECT_100_TIMEOUT_MS
OPT_PROXYHEADER = C.CURLOPT_PROXYHEADER
OPT_HEADEROPT = C.CURLOPT_HEADEROPT
OPT_PINNEDPUBLICKEY = C.CURLOPT_PINNEDPUBLICKEY
OPT_UNIX_SOCKET_PATH = C.CURLOPT_UNIX_SOCKET_PATH
OPT_SSL_VERIFYSTATUS = C.CURLOPT_SSL_VERIFYSTATUS
OPT_SSL_FALSESTART = C.CURLOPT_SSL_FALSESTART
OPT_PATH_AS_IS = C.CURLOPT_PATH_AS_IS
OPT_PROXY_SERVICE_NAME = C.CURLOPT_PROXY_SERVICE_NAME
OPT_SERVICE_NAME = C.CURLOPT_SERVICE_NAME
OPT_PIPEWAIT = C.CURLOPT_PIPEWAIT
OPT_DEFAULT_PROTOCOL = C.CURLOPT_DEFAULT_PROTOCOL
OPT_STREAM_WEIGHT = C.CURLOPT_STREAM_WEIGHT
OPT_STREAM_DEPENDS = C.CURLOPT_STREAM_DEPENDS
OPT_STREAM_DEPENDS_E = C.CURLOPT_STREAM_DEPENDS_E
OPT_TFTP_NO_OPTIONS = C.CURLOPT_TFTP_NO_OPTIONS
OPT_CONNECT_TO = C.CURLOPT_CONNECT_TO
OPT_TCP_FASTOPEN = C.CURLOPT_TCP_FASTOPEN
OPT_KEEP_SENDING_ON_ERROR = C.CURLOPT_KEEP_SENDING_ON_ERROR
OPT_PROXY_CAINFO = C.CURLOPT_PROXY_CAINFO
OPT_PROXY_CAPATH = C.CURLOPT_PROXY_CAPATH
OPT_PROXY_SSL_VERIFYPEER = C.CURLOPT_PROXY_SSL_VERIFYPEER
OPT_PROXY_SSL_VERIFYHOST = C.CURLOPT_PROXY_SSL_VERIFYHOST
OPT_PROXY_SSLVERSION = C.CURLOPT_PROXY_SSLVERSION
OPT_PROXY_TLSAUTH_USERNAME = C.CURLOPT_PROXY_TLSAUTH_USERNAME
OPT_PROXY_TLSAUTH_PASSWORD = C.CURLOPT_PROXY_TLSAUTH_PASSWORD
OPT_PROXY_TLSAUTH_TYPE = C.CURLOPT_PROXY_TLSAUTH_TYPE
OPT_PROXY_SSLCERT = C.CURLOPT_PROXY_SSLCERT
OPT_PROXY_SSLCERTTYPE = C.CURLOPT_PROXY_SSLCERTTYPE
OPT_PROXY_SSLKEY = C.CURLOPT_PROXY_SSLKEY
OPT_PROXY_SSLKEYTYPE = C.CURLOPT_PROXY_SSLKEYTYPE
OPT_PROXY_KEYPASSWD = C.CURLOPT_PROXY_KEYPASSWD
OPT_PROXY_SSL_CIPHER_LIST = C.CURLOPT_PROXY_SSL_CIPHER_LIST
OPT_PROXY_CRLFILE = C.CURLOPT_PROXY_CRLFILE
OPT_PROXY_SSL_OPTIONS = C.CURLOPT_PROXY_SSL_OPTIONS
OPT_PRE_PROXY = C.CURLOPT_PRE_PROXY
OPT_PROXY_PINNEDPUBLICKEY = C.CURLOPT_PROXY_PINNEDPUBLICKEY
OPT_ABSTRACT_UNIX_SOCKET = C.CURLOPT_ABSTRACT_UNIX_SOCKET
OPT_SUPPRESS_CONNECT_HEADERS = C.CURLOPT_SUPPRESS_CONNECT_HEADERS
OPT_REQUEST_TARGET = C.CURLOPT_REQUEST_TARGET
OPT_SOCKS5_AUTH = C.CURLOPT_SOCKS5_AUTH
OPT_SSH_COMPRESSION = C.CURLOPT_SSH_COMPRESSION
OPT_MIMEPOST = C.CURLOPT_MIMEPOST
OPT_POST301 = C.CURLOPT_POST301
OPT_SSLKEYPASSWD = C.CURLOPT_SSLKEYPASSWD
OPT_FTPAPPEND = C.CURLOPT_FTPAPPEND
OPT_FTPLISTONLY = C.CURLOPT_FTPLISTONLY
OPT_FTP_SSL = C.CURLOPT_FTP_SSL
OPT_SSLCERTPASSWD = C.CURLOPT_SSLCERTPASSWD
OPT_KRB4LEVEL = C.CURLOPT_KRB4LEVEL
OPT_RTSPHEADER = C.CURLOPT_RTSPHEADER
)
// easy.Getinfo(flag)
const (
INFO_TEXT = C.CURLINFO_TEXT
INFO_EFFECTIVE_URL = C.CURLINFO_EFFECTIVE_URL
INFO_RESPONSE_CODE = C.CURLINFO_RESPONSE_CODE
INFO_TOTAL_TIME = C.CURLINFO_TOTAL_TIME
INFO_NAMELOOKUP_TIME = C.CURLINFO_NAMELOOKUP_TIME
INFO_CONNECT_TIME = C.CURLINFO_CONNECT_TIME
INFO_PRETRANSFER_TIME = C.CURLINFO_PRETRANSFER_TIME
INFO_SIZE_UPLOAD = C.CURLINFO_SIZE_UPLOAD
INFO_SIZE_UPLOAD_T = C.CURLINFO_SIZE_UPLOAD_T
INFO_SIZE_DOWNLOAD = C.CURLINFO_SIZE_DOWNLOAD
INFO_SIZE_DOWNLOAD_T = C.CURLINFO_SIZE_DOWNLOAD_T
INFO_SPEED_DOWNLOAD = C.CURLINFO_SPEED_DOWNLOAD
INFO_SPEED_DOWNLOAD_T = C.CURLINFO_SPEED_DOWNLOAD_T
INFO_SPEED_UPLOAD = C.CURLINFO_SPEED_UPLOAD
INFO_SPEED_UPLOAD_T = C.CURLINFO_SPEED_UPLOAD_T
INFO_HEADER_SIZE = C.CURLINFO_HEADER_SIZE
INFO_REQUEST_SIZE = C.CURLINFO_REQUEST_SIZE
INFO_SSL_VERIFYRESULT = C.CURLINFO_SSL_VERIFYRESULT
INFO_FILETIME = C.CURLINFO_FILETIME
INFO_CONTENT_LENGTH_DOWNLOAD = C.CURLINFO_CONTENT_LENGTH_DOWNLOAD
INFO_CONTENT_LENGTH_DOWNLOAD_T = C.CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
INFO_CONTENT_LENGTH_UPLOAD = C.CURLINFO_CONTENT_LENGTH_UPLOAD
INFO_CONTENT_LENGTH_UPLOAD_T = C.CURLINFO_CONTENT_LENGTH_UPLOAD_T
INFO_STARTTRANSFER_TIME = C.CURLINFO_STARTTRANSFER_TIME
INFO_CONTENT_TYPE = C.CURLINFO_CONTENT_TYPE
INFO_REDIRECT_TIME = C.CURLINFO_REDIRECT_TIME
INFO_REDIRECT_COUNT = C.CURLINFO_REDIRECT_COUNT
INFO_PRIVATE = C.CURLINFO_PRIVATE
INFO_HTTP_CONNECTCODE = C.CURLINFO_HTTP_CONNECTCODE
INFO_HTTPAUTH_AVAIL = C.CURLINFO_HTTPAUTH_AVAIL
INFO_PROXYAUTH_AVAIL = C.CURLINFO_PROXYAUTH_AVAIL
INFO_OS_ERRNO = C.CURLINFO_OS_ERRNO
INFO_NUM_CONNECTS = C.CURLINFO_NUM_CONNECTS
INFO_SSL_ENGINES = C.CURLINFO_SSL_ENGINES
INFO_COOKIELIST = C.CURLINFO_COOKIELIST
INFO_LASTSOCKET = C.CURLINFO_LASTSOCKET
INFO_FTP_ENTRY_PATH = C.CURLINFO_FTP_ENTRY_PATH
INFO_REDIRECT_URL = C.CURLINFO_REDIRECT_URL
INFO_PRIMARY_IP = C.CURLINFO_PRIMARY_IP
INFO_APPCONNECT_TIME = C.CURLINFO_APPCONNECT_TIME
INFO_CERTINFO = C.CURLINFO_CERTINFO
INFO_CONDITION_UNMET = C.CURLINFO_CONDITION_UNMET
INFO_RTSP_SESSION_ID = C.CURLINFO_RTSP_SESSION_ID
INFO_RTSP_CLIENT_CSEQ = C.CURLINFO_RTSP_CLIENT_CSEQ
INFO_RTSP_SERVER_CSEQ = C.CURLINFO_RTSP_SERVER_CSEQ
INFO_RTSP_CSEQ_RECV = C.CURLINFO_RTSP_CSEQ_RECV
INFO_PRIMARY_PORT = C.CURLINFO_PRIMARY_PORT
INFO_LOCAL_IP = C.CURLINFO_LOCAL_IP
INFO_LOCAL_PORT = C.CURLINFO_LOCAL_PORT
INFO_TLS_SESSION = C.CURLINFO_TLS_SESSION
INFO_ACTIVESOCKET = C.CURLINFO_ACTIVESOCKET
INFO_TLS_SSL_PTR = C.CURLINFO_TLS_SSL_PTR
INFO_HTTP_VERSION = C.CURLINFO_HTTP_VERSION
INFO_PROXY_SSL_VERIFYRESULT = C.CURLINFO_PROXY_SSL_VERIFYRESULT
INFO_PROTOCOL = C.CURLINFO_PROTOCOL
INFO_SCHEME = C.CURLINFO_SCHEME
INFO_LASTONE = C.CURLINFO_LASTONE
INFO_HTTP_CODE = C.CURLINFO_HTTP_CODE
)
// Auth
const (
AUTH_NONE = C.CURLAUTH_NONE & (1<<32 - 1)
AUTH_BASIC = C.CURLAUTH_BASIC & (1<<32 - 1)
AUTH_DIGEST = C.CURLAUTH_DIGEST & (1<<32 - 1)
AUTH_NEGOTIATE = C.CURLAUTH_NEGOTIATE & (1<<32 - 1)
AUTH_GSSNEGOTIATE = C.CURLAUTH_GSSNEGOTIATE & (1<<32 - 1)
AUTH_GSSAPI = C.CURLAUTH_GSSAPI & (1<<32 - 1)
AUTH_NTLM = C.CURLAUTH_NTLM & (1<<32 - 1)
AUTH_DIGEST_IE = C.CURLAUTH_DIGEST_IE & (1<<32 - 1)
AUTH_NTLM_WB = C.CURLAUTH_NTLM_WB & (1<<32 - 1)
AUTH_ONLY = C.CURLAUTH_ONLY & (1<<32 - 1)
AUTH_ANY = C.CURLAUTH_ANY & (1<<32 - 1)
AUTH_ANYSAFE = C.CURLAUTH_ANYSAFE & (1<<32 - 1)
)
// generated ends
-118
View File
@@ -1,118 +0,0 @@
// libcurl go bingding
package curl
/*
#cgo linux pkg-config: libcurl
#cgo darwin LDFLAGS: -lcurl
#cgo windows LDFLAGS: -lcurl
#include <stdlib.h>
#include <curl/curl.h>
static char *string_array_index(char **p, int i) {
return p[i];
}
*/
import "C"
import (
"time"
"unsafe"
)
// curl_global_init - Global libcurl initialisation
func GlobalInit(flags int) error {
return newCurlError(C.curl_global_init(C.long(flags)))
}
// curl_global_cleanup - global libcurl cleanup
func GlobalCleanup() {
C.curl_global_cleanup()
}
type VersionInfoData struct {
Age C.CURLversion
// age >= 0
Version string
VersionNum uint
Host string
Features int
SslVersion string
SslVersionNum int
LibzVersion string
Protocols []string
// age >= 1
Ares string
AresNum int
// age >= 2
Libidn string
// age >= 3
IconvVerNum int
LibsshVersion string
}
// curl_version - returns the libcurl version string
func Version() string {
return C.GoString(C.curl_version())
}
// curl_version_info - returns run-time libcurl version info
func VersionInfo(ver C.CURLversion) *VersionInfoData {
data := C.curl_version_info(ver)
ret := new(VersionInfoData)
ret.Age = data.age
switch age := ret.Age; {
case age >= 0:
ret.Version = string(C.GoString(data.version))
ret.VersionNum = uint(data.version_num)
ret.Host = C.GoString(data.host)
ret.Features = int(data.features)
ret.SslVersion = C.GoString(data.ssl_version)
ret.SslVersionNum = int(data.ssl_version_num)
ret.LibzVersion = C.GoString(data.libz_version)
// ugly but works
ret.Protocols = []string{}
for i := C.int(0); C.string_array_index(data.protocols, i) != nil; i++ {
p := C.string_array_index(data.protocols, i)
ret.Protocols = append(ret.Protocols, C.GoString(p))
}
fallthrough
case age >= 1:
ret.Ares = C.GoString(data.ares)
ret.AresNum = int(data.ares_num)
fallthrough
case age >= 2:
ret.Libidn = C.GoString(data.libidn)
fallthrough
case age >= 3:
ret.IconvVerNum = int(data.iconv_ver_num)
ret.LibsshVersion = C.GoString(data.libssh_version)
}
return ret
}
// curl_getdate - Convert a date string to number of seconds since January 1, 1970
// In golang, we convert it to a *time.Time
func Getdate(date string) *time.Time {
datestr := C.CString(date)
defer C.free(unsafe.Pointer(datestr))
t := C.curl_getdate(datestr, nil)
if t == -1 {
return nil
}
unix := time.Unix(int64(t), 0).UTC()
return &unix
/*
// curl_getenv - return value for environment name
func Getenv(name string) string {
namestr := C.CString(name)
defer C.free(unsafe.Pointer(namestr))
ret := C.curl_getenv(unsafe.Pointer(namestr))
defer C.free(unsafe.Pointer(ret))
return C.GoString(ret)
}
*/
}
// TODO: curl_global_init_mem
-23
View File
@@ -1,23 +0,0 @@
package curl
import (
"testing"
)
func TestVersionInfo(t *testing.T) {
info := VersionInfo(VERSION_FIRST)
expectedProtocols := []string{"dict", "file", "ftp", "ftps", "gopher", "http", "https", "imap", "imaps", "ldap", "ldaps", "pop3", "pop3s", "rtmp", "rtsp", "smtp", "smtps", "telnet", "tftp", "scp", "sftp", "smb", "smbs"}
protocols := info.Protocols
for _, protocol := range protocols {
found := false
for _, expectedProtocol := range expectedProtocols {
if expectedProtocol == protocol {
found = true
break
}
}
if !found {
t.Errorf("protocol should be in %v and is %v.", expectedProtocols, protocol)
}
}
}
-527
View File
@@ -1,527 +0,0 @@
package curl
/*
#include <stdlib.h>
#include <curl/curl.h>
#include "callback.h"
#include "compat.h"
static CURLcode curl_easy_setopt_long(CURL *handle, CURLoption option, long parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_string(CURL *handle, CURLoption option, char *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_slist(CURL *handle, CURLoption option, struct curl_slist *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_pointer(CURL *handle, CURLoption option, void *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_off_t(CURL *handle, CURLoption option, off_t parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_getinfo_string(CURL *curl, CURLINFO info, char **p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_long(CURL *curl, CURLINFO info, long *p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_double(CURL *curl, CURLINFO info, double *p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_slist(CURL *curl, CURLINFO info, struct curl_slist **p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLFORMcode curl_formadd_name_content_length(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_COPYCONTENTS, content,
CURLFORM_CONTENTSLENGTH, length, CURLFORM_END);
}
static CURLFORMcode curl_formadd_name_content_length_type(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length, char *type) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_COPYCONTENTS, content,
CURLFORM_CONTENTSLENGTH, length,
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
}
static CURLFORMcode curl_formadd_name_file_type(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *filename, char *type) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_FILE, filename,
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
}
// TODO: support multi file
*/
import "C"
import (
"fmt"
"mime"
"path"
"unsafe"
"sync"
)
type CurlInfo C.CURLINFO
type CurlError C.CURLcode
type CurlString *C.char
func NewCurlString(s string) CurlString {
return CurlString(unsafe.Pointer(C.CString(s)))
}
func FreeCurlString(s CurlString) {
C.free(unsafe.Pointer(s))
}
func (e CurlError) Error() string {
// ret is const char*, no need to free
ret := C.curl_easy_strerror(C.CURLcode(e))
return fmt.Sprintf("curl: %s", C.GoString(ret))
}
func newCurlError(errno C.CURLcode) error {
if errno == C.CURLE_OK { // if nothing wrong
return nil
}
return CurlError(errno)
}
// curl_easy interface
type CURL struct {
handle unsafe.Pointer
// callback functions, bool ret means ok or not
headerFunction, writeFunction *func([]byte, interface{}) bool
readFunction *func([]byte, interface{}) int // return num of bytes writed to buf
progressFunction *func(float64, float64, float64, float64, interface{}) bool
fnmatchFunction *func(string, string, interface{}) int
// callback datas
headerData, writeData, readData, progressData, fnmatchData interface{}
// list of C allocs
mallocAllocs []*C.char
}
// concurrent safe context map
type contextMap struct {
items map[uintptr]*CURL
sync.RWMutex
}
func (c *contextMap) Set(k uintptr, v *CURL) {
c.Lock()
defer c.Unlock()
c.items[k] = v
}
func (c *contextMap) Get(k uintptr) *CURL {
c.RLock()
defer c.RUnlock()
return c.items[k]
}
func (c *contextMap) Delete(k uintptr) {
c.Lock()
defer c.Unlock()
delete(c.items, k)
}
var context_map = &contextMap {
items: make(map[uintptr]*CURL),
}
// curl_easy_init - Start a libcurl easy session
func EasyInit() *CURL {
p := C.curl_easy_init()
c := &CURL{handle: p, mallocAllocs: make([]*C.char, 0)} // other field defaults to nil
context_map.Set(uintptr(p), c)
return c
}
// curl_easy_duphandle - Clone a libcurl session handle
func (curl *CURL) Duphandle() *CURL {
p := C.curl_easy_duphandle(curl.handle)
c := &CURL{handle: p}
context_map.Set(uintptr(p), c)
return c
}
// curl_easy_cleanup - End a libcurl easy session
func (curl *CURL) Cleanup() {
p := curl.handle
C.curl_easy_cleanup(p)
curl.MallocFreeAfter(0)
context_map.Delete(uintptr(p))
}
// curl_easy_setopt - set options for a curl easy handle
// WARNING: a function pointer is &fun, but function addr is reflect.ValueOf(fun).Pointer()
func (curl *CURL) Setopt(opt int, param interface{}) error {
p := curl.handle
if param == nil {
// NOTE: some option will crash program when got a nil param
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), nil))
}
switch {
// not really set
case opt == OPT_READDATA: // OPT_INFILE
curl.readData = param
return nil
case opt == OPT_PROGRESSDATA:
curl.progressData = param
return nil
case opt == OPT_HEADERDATA: // also known as OPT_WRITEHEADER
curl.headerData = param
return nil
case opt == OPT_WRITEDATA: // OPT_FILE
curl.writeData = param
return nil
case opt == OPT_READFUNCTION:
fun := param.(func([]byte, interface{}) int)
curl.readFunction = &fun
ptr := C.return_read_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_READDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_PROGRESSFUNCTION:
fun := param.(func(float64, float64, float64, float64, interface{}) bool)
curl.progressFunction = &fun
ptr := C.return_progress_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_PROGRESSDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_HEADERFUNCTION:
fun := param.(func([]byte, interface{}) bool)
curl.headerFunction = &fun
ptr := C.return_header_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_HEADERDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_WRITEFUNCTION:
fun := param.(func([]byte, interface{}) bool)
curl.writeFunction = &fun
ptr := C.return_write_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_WRITEDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
// for OPT_HTTPPOST, use struct Form
case opt == OPT_HTTPPOST:
post := param.(*Form)
ptr := post.head
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), unsafe.Pointer(ptr)))
case opt >= C.CURLOPTTYPE_OFF_T:
val := C.off_t(0)
switch t := param.(type) {
case int:
val = C.off_t(t)
case uint64:
val = C.off_t(t)
default:
panic("OFF_T conversion not supported")
}
return newCurlError(C.curl_easy_setopt_off_t(p, C.CURLoption(opt), val))
case opt >= C.CURLOPTTYPE_FUNCTIONPOINT:
// function pointer
panic("function pointer not implemented yet!")
case opt >= C.CURLOPTTYPE_OBJECTPOINT:
switch t := param.(type) {
case string:
ptr := C.CString(t)
curl.mallocAddPtr(ptr)
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
case CurlString:
ptr := (*C.char)(t)
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
case []string:
if len(t) > 0 {
ptr := C.CString(t[0])
curl.mallocAddPtr(ptr)
a_slist := C.curl_slist_append(nil, ptr)
for _, s := range t[1:] {
ptr := C.CString(s)
curl.mallocAddPtr(ptr)
a_slist = C.curl_slist_append(a_slist, ptr)
}
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
} else {
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
}
case []CurlString:
if len(t) > 0 {
ptr := (*C.char)(t[0])
a_slist := C.curl_slist_append(nil, ptr)
for _, s := range t[1:] {
ptr := (*C.char)(s)
a_slist = C.curl_slist_append(a_slist, ptr)
}
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
} else {
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
}
default:
// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
// val := reflect.ValueOf(param)
//fmt.Printf("DEBUG(Setopt): param=%x\n", val.Pointer())
//println("DEBUG can addr =", val.Pointer(), "opt=", opt)
// pass a pointer to GoInterface
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt),
unsafe.Pointer(&param)))
}
case opt >= C.CURLOPTTYPE_LONG:
val := C.long(0)
switch t := param.(type) {
case int:
val = C.long(t)
case bool:
if t {
val = 1
}
case int64:
val = C.long(t)
case int32:
val = C.long(t)
default:
panic("not supported converstion to c long")
}
return newCurlError(C.curl_easy_setopt_long(p, C.CURLoption(opt), val))
}
panic("opt param error!")
}
// curl_easy_send - sends raw data over an "easy" connection
func (curl *CURL) Send(buffer []byte) (int, error) {
p := curl.handle
buflen := len(buffer)
n := C.size_t(0)
ret := C.curl_easy_send(p, unsafe.Pointer(&buffer[0]), C.size_t(buflen), &n)
return int(n), newCurlError(ret)
}
// curl_easy_recv - receives raw data on an "easy" connection
func (curl *CURL) Recv(buffer []byte) (int, error) {
p := curl.handle
buflen := len(buffer)
buf := C.CString(string(buffer))
n := C.size_t(0)
ret := C.curl_easy_recv(p, unsafe.Pointer(buf), C.size_t(buflen), &n)
return copy(buffer, C.GoStringN(buf, C.int(n))), newCurlError(ret)
}
// curl_easy_perform - Perform a file transfer
func (curl *CURL) Perform() error {
p := curl.handle
return newCurlError(C.curl_easy_perform(p))
}
// curl_easy_pause - pause and unpause a connection
func (curl *CURL) Pause(bitmask int) error {
p := curl.handle
return newCurlError(C.curl_easy_pause(p, C.int(bitmask)))
}
// curl_easy_reset - reset all options of a libcurl session handle
func (curl *CURL) Reset() {
p := curl.handle
C.curl_easy_reset(p)
}
// curl_easy_escape - URL encodes the given string
func (curl *CURL) Escape(url string) string {
p := curl.handle
oldUrl := C.CString(url)
defer C.free(unsafe.Pointer(oldUrl))
newUrl := C.curl_easy_escape(p, oldUrl, 0)
defer C.curl_free(unsafe.Pointer(newUrl))
return C.GoString(newUrl)
}
// curl_easy_unescape - URL decodes the given string
func (curl *CURL) Unescape(url string) string {
p := curl.handle
oldUrl := C.CString(url)
outlength := C.int(0)
defer C.free(unsafe.Pointer(oldUrl))
// If outlength is non-NULL, the function will write the length of the
// returned string in the integer it points to. This allows an
// escaped string containing %00 to still get used properly after unescaping.
newUrl := C.curl_easy_unescape(p, oldUrl, 0, &outlength)
defer C.curl_free(unsafe.Pointer(newUrl))
return C.GoStringN(newUrl, outlength)
}
// curl_easy_getinfo - extract information from a curl handle
func (curl *CURL) Getinfo(info CurlInfo) (ret interface{}, err error) {
p := curl.handle
cInfo := C.CURLINFO(info)
switch cInfo & C.CURLINFO_TYPEMASK {
case C.CURLINFO_STRING:
a_string := C.CString("")
defer C.free(unsafe.Pointer(a_string))
err := newCurlError(C.curl_easy_getinfo_string(p, cInfo, &a_string))
ret := C.GoString(a_string)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_LONG:
a_long := C.long(-1)
err := newCurlError(C.curl_easy_getinfo_long(p, cInfo, &a_long))
ret := int(a_long)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_DOUBLE:
a_double := C.double(0.0)
err := newCurlError(C.curl_easy_getinfo_double(p, cInfo, &a_double))
ret := float64(a_double)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_SLIST:
a_ptr_slist := new(_Ctype_struct_curl_slist)
err := newCurlError(C.curl_easy_getinfo_slist(p, cInfo, &a_ptr_slist))
ret := []string{}
for a_ptr_slist != nil {
debugf("Getinfo %s %v", C.GoString(a_ptr_slist.data), a_ptr_slist.next)
ret = append(ret, C.GoString(a_ptr_slist.data))
a_ptr_slist = a_ptr_slist.next
}
return ret, err
default:
panic("error calling Getinfo\n")
}
panic("not implemented yet!")
return nil, nil
}
func (curl *CURL) GetHandle() unsafe.Pointer {
return curl.handle
}
func (curl *CURL) MallocGetPos() int {
return len(curl.mallocAllocs)
}
func (curl *CURL) MallocFreeAfter(from int) {
l := len(curl.mallocAllocs)
for idx := from; idx < l; idx++ {
C.free(unsafe.Pointer(curl.mallocAllocs[idx]))
curl.mallocAllocs[idx] = nil
}
curl.mallocAllocs = curl.mallocAllocs[0:from]
}
func (curl *CURL) mallocAddPtr(ptr *C.char) {
curl.mallocAllocs = append(curl.mallocAllocs, ptr)
}
// A multipart/formdata HTTP POST form
type Form struct {
head, last *C.struct_curl_httppost
}
func NewForm() *Form {
return &Form{}
}
func (form *Form) Add(name string, content interface{}) error {
head, last := form.head, form.last
namestr := C.CString(name)
defer C.free(unsafe.Pointer(namestr))
var (
buffer *C.char
length C.int
)
switch t := content.(type) {
case string:
buffer = C.CString(t)
length = C.int(len(t))
case []byte:
buffer = C.CString(string(t))
length = C.int(len(t))
default:
panic("not implemented")
}
defer C.free(unsafe.Pointer(buffer))
C.curl_formadd_name_content_length(&head, &last, namestr, buffer, length)
form.head, form.last = head, last
return nil
}
func (form *Form) AddWithType(name string, content interface{}, content_type string) error {
head, last := form.head, form.last
namestr := C.CString(name)
typestr := C.CString(content_type)
defer C.free(unsafe.Pointer(namestr))
defer C.free(unsafe.Pointer(typestr))
var (
buffer *C.char
length C.int
)
switch t := content.(type) {
case string:
buffer = C.CString(t)
length = C.int(len(t))
case []byte:
buffer = C.CString(string(t))
length = C.int(len(t))
default:
panic("not implemented")
}
defer C.free(unsafe.Pointer(buffer))
C.curl_formadd_name_content_length_type(&head, &last, namestr, buffer, length, typestr)
form.head, form.last = head, last
return nil
}
func (form *Form) AddFile(name, filename string) error {
head, last := form.head, form.last
namestr := C.CString(name)
pathstr := C.CString(filename)
typestr := C.CString(guessType(filename))
defer C.free(unsafe.Pointer(namestr))
defer C.free(unsafe.Pointer(pathstr))
defer C.free(unsafe.Pointer(typestr))
C.curl_formadd_name_file_type(&head, &last, namestr, pathstr, typestr)
form.head, form.last = head, last
return nil
}
func (form *Form) AddFromFile(name, filename string) {
}
func guessType(filename string) string {
ext := path.Ext(filename)
file_type := mime.TypeByExtension(ext)
if file_type == "" {
return "application/octet-stream"
}
return file_type
}
-77
View File
@@ -1,77 +0,0 @@
package curl
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"sync"
)
func setupTestServer(serverContent string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, serverContent)
}))
}
func TestEasyInterface(t *testing.T) {
ts := setupTestServer("")
defer ts.Close()
easy := EasyInit()
defer easy.Cleanup()
easy.Setopt(OPT_URL, ts.URL)
if err := easy.Perform(); err != nil {
t.Fatal(err)
}
}
func TestCallbackFunction(t *testing.T) {
serverContent := "A random string"
ts := setupTestServer(serverContent)
defer ts.Close()
easy := EasyInit()
defer easy.Cleanup()
easy.Setopt(OPT_URL, ts.URL)
easy.Setopt(OPT_WRITEFUNCTION, func(buf []byte, userdata interface{}) bool {
result := string(buf)
expected := serverContent + "\n"
if result != expected {
t.Errorf("output should be %q and is %q.", expected, result)
}
return true
})
if err := easy.Perform(); err != nil {
t.Fatal(err)
}
}
func TestEscape(t *testing.T) {
easy := EasyInit()
defer easy.Cleanup()
payload := `payload={"msg": "First line\nSecond Line"}`
expected := `payload%3D%7B%22msg%22%3A%20%22First%20line%5CnSecond%20Line%22%7D`
result := easy.Escape(payload)
if result != expected {
t.Errorf("escaped output should be %q and is %q.", expected, result)
}
}
func TestConcurrentInitAndCleanup(t *testing.T) {
c := 2
var wg sync.WaitGroup
wg.Add(c)
for i := 0; i < c; i++ {
go func() {
wg.Done()
easy := EasyInit()
defer easy.Cleanup()
}()
}
wg.Wait()
}
-56
View File
@@ -1,56 +0,0 @@
package curl
import (
"log"
)
const (
_DEBUG = 10 * (iota + 1)
_INFO
_WARN
_ERROR
)
const _DEFAULT_LOG_LEVEL = _WARN
var log_level = _DEFAULT_LOG_LEVEL
// SetLogLevel changes the log level which determines the granularity of the
// messages that are logged. Available log levels are: "DEBUG", "INFO",
// "WARN", "ERROR" and "DEFAULT_LOG_LEVEL".
func SetLogLevel(levelName string) {
switch levelName {
case "DEBUG":
log_level = _DEBUG
case "INFO":
log_level = _INFO
case "WARN":
log_level = _WARN
case "ERROR":
log_level = _ERROR
case "DEFAULT_LOG_LEVEL":
log_level = _DEFAULT_LOG_LEVEL
}
}
func logf(limitLevel int, format string, args ...interface{}) {
if log_level <= limitLevel {
log.Printf(format, args...)
}
}
func debugf(format string, args ...interface{}) {
logf(_DEBUG, format, args...)
}
func infof(format string, args ...interface{}) {
logf(_INFO, format, args...)
}
func warnf(format string, args ...interface{}) {
logf(_WARN, format, args...)
}
func errorf(format string, args ...interface{}) {
logf(_ERROR, format, args...)
}
-64
View File
@@ -1,64 +0,0 @@
package curl
import (
"testing"
"bytes"
"log"
"os"
"fmt"
"regexp"
)
func TestDefaultLogLevel(t *testing.T) {
if log_level != _DEFAULT_LOG_LEVEL {t.Error("Test failed, expected DEFAULT_LOG_LEVEL level.")}
}
func TestSetLogLevel(t *testing.T) {
SetLogLevel("DEBUG")
defer SetLogLevel("DEFAULT_LOG_LEVEL")
if log_level != _DEBUG {t.Error("Test failed, expected DEBUG level.")}
SetLogLevel("INFO")
if log_level != _INFO {t.Error("Test failed, expected INFO level.")}
SetLogLevel("WARN")
if log_level != _WARN {t.Error("Test failed, expected WARN level.")}
SetLogLevel("ERROR")
if log_level != _ERROR {t.Error("Test failed, expected ERROR level.")}
}
var (
testFormat = "test format %s"
testArgument = "test string 1"
expectedRegexp = regexp.MustCompile(".*" + fmt.Sprintf(testFormat, testArgument) + "\n$")
)
func TestLogf(t *testing.T) {
buf := new(bytes.Buffer)
log.SetOutput(buf)
defer log.SetOutput(os.Stderr)
SetLogLevel("DEBUG")
defer SetLogLevel("DEFAULT_LOG_LEVEL")
logf(_DEBUG, testFormat, testArgument)
line := buf.String()
matched := expectedRegexp.MatchString(line)
if !matched {
t.Errorf("log output should match %q and is %q.", expectedRegexp, line)
}
}
func TestLogfUsesLogLevel(t *testing.T) {
buf := new(bytes.Buffer)
log.SetOutput(buf)
defer log.SetOutput(os.Stderr)
SetLogLevel("WARN")
defer SetLogLevel("DEFAULT_LOG_LEVEL")
logf(_DEBUG, testFormat, testArgument)
line := buf.String()
expectedLine := ""
if line != expectedLine {
t.Errorf("log output should match %q and is %q.", expectedLine, line)
}
}
-160
View File
@@ -1,160 +0,0 @@
// This file depends on functionality not available on Windows, hence we
// must skip it. https://github.com/andelf/go-curl/issues/48
// +build !windows
package curl
/*
#include <stdlib.h>
#include <curl/curl.h>
static CURLMcode curl_multi_setopt_long(CURLM *handle, CURLMoption option, long parameter) {
return curl_multi_setopt(handle, option, parameter);
}
static CURLMcode curl_multi_setopt_pointer(CURLM *handle, CURLMoption option, void *parameter) {
return curl_multi_setopt(handle, option, parameter);
}
static CURLMcode curl_multi_fdset_pointer(CURLM *handle,
void *read_fd_set,
void *write_fd_set,
void *exc_fd_set,
int *max_fd)
{
return curl_multi_fdset(handle, read_fd_set, write_fd_set, exc_fd_set, max_fd);
}
static CURLMsg *curl_multi_info_read_pointer(CURLM *handle, int *msgs_in_queue)
{
return curl_multi_info_read(handle, msgs_in_queue);
}
*/
import "C"
import (
"unsafe"
"syscall"
)
type CurlMultiError C.CURLMcode
type CurlMultiMsg C.CURLMSG
func (e CurlMultiError) Error() string {
// ret is const char*, no need to free
ret := C.curl_multi_strerror(C.CURLMcode(e))
return C.GoString(ret)
}
func newCurlMultiError(errno C.CURLMcode) error {
// cannot use C.CURLM_OK here, cause multi.h use a undefined emum num
if errno == 0 { // if nothing wrong
return nil
}
return CurlMultiError(errno)
}
func newCURLMessage(message *C.CURLMsg) (msg *CURLMessage){
if message == nil {
return nil
}
msg = new(CURLMessage)
msg.Msg = CurlMultiMsg(message.msg)
msg.Easy_handle = &CURL{handle: message.easy_handle}
msg.Data = message.data
return msg
}
type CURLM struct {
handle unsafe.Pointer
}
var dummy unsafe.Pointer
type CURLMessage struct {
Msg CurlMultiMsg
Easy_handle *CURL
Data [unsafe.Sizeof(dummy)]byte
}
// curl_multi_init - create a multi handle
func MultiInit() *CURLM {
p := C.curl_multi_init()
return &CURLM{p}
}
// curl_multi_cleanup - close down a multi session
func (mcurl *CURLM) Cleanup() error {
p := mcurl.handle
return newCurlMultiError(C.curl_multi_cleanup(p))
}
// curl_multi_perform - reads/writes available data from each easy handle
func (mcurl *CURLM) Perform() (int, error) {
p := mcurl.handle
running_handles := C.int(-1)
err := newCurlMultiError(C.curl_multi_perform(p, &running_handles))
return int(running_handles), err
}
// curl_multi_add_handle - add an easy handle to a multi session
func (mcurl *CURLM) AddHandle(easy *CURL) error {
mp := mcurl.handle
easy_handle := easy.handle
return newCurlMultiError(C.curl_multi_add_handle(mp, easy_handle))
}
// curl_multi_remove_handle - remove an easy handle from a multi session
func (mcurl *CURLM) RemoveHandle(easy *CURL) error {
mp := mcurl.handle
easy_handle := easy.handle
return newCurlMultiError(C.curl_multi_remove_handle(mp, easy_handle))
}
func (mcurl *CURLM) Timeout() (int, error) {
p := mcurl.handle
timeout := C.long(-1)
err := newCurlMultiError(C.curl_multi_timeout(p, &timeout))
return int(timeout), err
}
func (mcurl *CURLM) Setopt(opt int, param interface{}) error {
p := mcurl.handle
if param == nil {
return newCurlMultiError(C.curl_multi_setopt_pointer(p, C.CURLMoption(opt), nil))
}
switch {
// currently cannot support these option
// case MOPT_SOCKETFUNCTION, MOPT_SOCKETDATA, MOPT_TIMERFUNCTION, MOPT_TIMERDATA:
// panic("not supported CURLM.Setopt opt")
case opt >= C.CURLOPTTYPE_LONG:
val := C.long(0)
switch t := param.(type) {
case int:
val := C.long(t)
return newCurlMultiError(C.curl_multi_setopt_long(p, C.CURLMoption(opt), val))
case bool:
val = C.long(0)
if t {
val = C.long(1)
}
return newCurlMultiError(C.curl_multi_setopt_long(p, C.CURLMoption(opt), val))
}
}
panic("not supported CURLM.Setopt opt or param")
return nil
}
func (mcurl *CURLM) Fdset(rset, wset, eset *syscall.FdSet) (int, error) {
p := mcurl.handle
read := unsafe.Pointer(rset)
write := unsafe.Pointer(wset)
exc := unsafe.Pointer(eset)
maxfd := C.int(-1)
err := newCurlMultiError(C.curl_multi_fdset_pointer(p, read, write,
exc, &maxfd))
return int(maxfd), err
}
func (mcurl *CURLM) Info_read() (*CURLMessage, int) {
p := mcurl.handle
left := C.int(0)
return newCURLMessage(C.curl_multi_info_read_pointer(p, &left)), int(left)
}
-62
View File
@@ -1,62 +0,0 @@
package curl
/*
#include <curl/curl.h>
static CURLSHcode curl_share_setopt_long(CURLSH *handle, CURLSHoption option, long parameter) {
return curl_share_setopt(handle, option, parameter);
}
static CURLSHcode curl_share_setopt_pointer(CURLSH *handle, CURLSHoption option, void *parameter) {
return curl_share_setopt(handle, option, parameter);
}
*/
import "C"
import "unsafe"
// implement os.Error interface
type CurlShareError C.CURLMcode
func (e CurlShareError) Error() string {
// ret is const char*, no need to free
ret := C.curl_share_strerror(C.CURLSHcode(e))
return C.GoString(ret)
}
func newCurlShareError(errno C.CURLSHcode) error {
if errno == 0 { // if nothing wrong
return nil
}
return CurlShareError(errno)
}
type CURLSH struct {
handle unsafe.Pointer
}
func ShareInit() *CURLSH {
p := C.curl_share_init()
return &CURLSH{p}
}
func (shcurl *CURLSH) Cleanup() error {
p := shcurl.handle
return newCurlShareError(C.curl_share_cleanup(p))
}
func (shcurl *CURLSH) Setopt(opt int, param interface{}) error {
p := shcurl.handle
if param == nil {
return newCurlShareError(C.curl_share_setopt_pointer(p, C.CURLSHoption(opt), nil))
}
switch opt {
// case SHOPT_LOCKFUNC, SHOPT_UNLOCKFUNC, SHOPT_USERDATA:
// panic("not supported")
case SHOPT_SHARE, SHOPT_UNSHARE:
if val, ok := param.(int); ok {
return newCurlShareError(C.curl_share_setopt_long(p, C.CURLSHoption(opt), C.long(val)))
}
}
panic("not supported CURLSH.Setopt opt or param")
return nil
}
-22
View File
@@ -1,22 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
-28
View File
@@ -1,28 +0,0 @@
language: go
go_import_path: github.com/davecgh/go-spew
go:
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- tip
sudo: false
install:
- go get -v github.com/alecthomas/gometalinter
- gometalinter --install
script:
- export PATH=$PATH:$HOME/gopath/bin
- export GORACE="halt_on_error=1"
- test -z "$(gometalinter --disable-all
--enable=gofmt
--enable=golint
--enable=vet
--enable=gosimple
--enable=unconvert
--deadline=4m ./spew | tee /dev/stderr)"
- go test -v -race -tags safe ./spew
- go test -v -race -tags testcgo ./spew -covermode=atomic -coverprofile=profile.cov
after_success:
- go get -v github.com/mattn/goveralls
- goveralls -coverprofile=profile.cov -service=travis-ci
-15
View File
@@ -1,15 +0,0 @@
ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
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.
-201
View File
@@ -1,201 +0,0 @@
go-spew
=======
[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)](https://travis-ci.org/davecgh/go-spew)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![Coverage Status](https://img.shields.io/coveralls/davecgh/go-spew.svg)](https://coveralls.io/r/davecgh/go-spew?branch=master)
Go-spew implements a deep pretty printer for Go data structures to aid in
debugging. A comprehensive suite of tests with 100% test coverage is provided
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
report. Go-spew is licensed under the liberal ISC license, so it may be used in
open source or commercial projects.
If you're interested in reading about how this package came to life and some
of the challenges involved in providing a deep pretty printer, there is a blog
post about it
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
## Documentation
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/davecgh/go-spew/spew)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the excellent GoDoc site here:
http://godoc.org/github.com/davecgh/go-spew/spew
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
## Installation
```bash
$ go get -u github.com/davecgh/go-spew/spew
```
## Quick Start
Add this import line to the file you're working in:
```Go
import "github.com/davecgh/go-spew/spew"
```
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
```Go
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
```
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
and pointer addresses):
```Go
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
```
## Debugging a Web Application Example
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
```Go
package main
import (
"fmt"
"html"
"net/http"
"github.com/davecgh/go-spew/spew"
)
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
```
## Sample Dump Output
```
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) {
(string) "one": (bool) true
}
}
([]uint8) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
```
## Sample Formatter Output
Double pointer to a uint8:
```
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
```
Pointer to circular struct with a uint8 field and a pointer to itself:
```
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
```
## Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available via the
spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
```
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables. This option
relies on access to the unsafe package, so it will not have any effect when
running in environments without access to the unsafe package such as Google
App Engine or with the "safe" build tag specified.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of capacities
for arrays, slices, maps and channels. This is useful when diffing data
structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are supported,
with other types sorted according to the reflect.Value.String() output
which guarantees display stability. Natural map order is used by
default.
* SpewKeys
SpewKeys specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only considered
if SortKeys is true.
```
## Unsafe Package Dependency
This package relies on the unsafe package to perform some of the more advanced
features, however it also supports a "limited" mode which allows it to work in
environments where the unsafe package is not available. By default, it will
operate in this mode on Google App Engine and when compiled with GopherJS. The
"safe" build tag may also be specified to force the package to build without
using the unsafe package.
## License
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
-22
View File
@@ -1,22 +0,0 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
if ! type gocov >/dev/null 2>&1; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
# Only run the cgo tests if gcc is installed.
if type gcc >/dev/null 2>&1; then
(cd spew && gocov test -tags testcgo | gocov report)
else
(cd spew && gocov test | gocov report)
fi
-145
View File
@@ -1,145 +0,0 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and 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.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
import (
"reflect"
"unsafe"
)
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = false
// ptrSize is the size of a pointer on the current arch.
ptrSize = unsafe.Sizeof((*byte)(nil))
)
type flag uintptr
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data. It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}
-38
View File
@@ -1,38 +0,0 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and 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.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe !go1.4
package spew
import "reflect"
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = true
)
// unsafeReflectValue typically converts the passed reflect.Value into a one
// that bypasses the typical safety restrictions preventing access to
// unaddressable and unexported data. However, doing this relies on access to
// the unsafe package. This is a stub version which simply returns the passed
// reflect.Value when the unsafe package is not available.
func unsafeReflectValue(v reflect.Value) reflect.Value {
return v
}
-341
View File
@@ -1,341 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
panicBytes = []byte("(PANIC=")
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface {})")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
asteriskBytes = []byte("*")
colonBytes = []byte(":")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
spaceBytes = []byte(" ")
pointerChainBytes = []byte("->")
nilAngleBytes = []byte("<nil>")
maxNewlineBytes = []byte("<max depth reached>\n")
maxShortBytes = []byte("<max>")
circularBytes = []byte("<already shown>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
precisionBytes = []byte(".")
openAngleBytes = []byte("<")
closeAngleBytes = []byte(">")
openMapBytes = []byte("map[")
closeMapBytes = []byte("]")
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// catchPanic handles any panics that might occur during the handleMethods
// calls.
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes)
fmt.Fprintf(w, "%v", err)
w.Write(closeParenBytes)
}
}
// handleMethods attempts to call the Error and String methods on the underlying
// type the passed reflect.Value represents and outputes the result to Writer w.
//
// It handles panics in any called methods by catching and displaying the error
// as the formatted value.
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe, when it's available,
// to bypass these restrictions since this package does not mutate the
// values.
if !v.CanInterface() {
if UnsafeDisabled {
return false
}
v = unsafeReflectValue(v)
}
// Choose whether or not to do error and Stringer interface lookups against
// the base type or a pointer to the base type depending on settings.
// Technically calling one of these methods with a pointer receiver can
// mutate the value, however, types which choose to satisify an error or
// Stringer interface with a pointer receiver should not be mutating their
// state inside these interface methods.
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
v = unsafeReflectValue(v)
}
if v.CanAddr() {
v = v.Addr()
}
// Is it an error or Stringer?
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.Error()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.Error()))
return true
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.String()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.String()))
return true
}
return false
}
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilAngleBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
values []reflect.Value
strings []string // either nil or same len and values
cs *ConfigState
}
// newValuesSorter initializes a valuesSorter instance, which holds a set of
// surrogate keys on which the data should be sorted. It uses flags in
// ConfigState to decide if and how to populate those surrogate keys.
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
vs := &valuesSorter{values: values, cs: cs}
if canSortSimply(vs.values[0].Kind()) {
return vs
}
if !cs.DisableMethods {
vs.strings = make([]string, len(values))
for i := range vs.values {
b := bytes.Buffer{}
if !handleMethods(cs, &b, vs.values[i]) {
vs.strings = nil
break
}
vs.strings[i] = b.String()
}
}
if vs.strings == nil && cs.SpewKeys {
vs.strings = make([]string, len(values))
for i := range vs.values {
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
}
}
return vs
}
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
// directly, or whether it should be considered for sorting by surrogate keys
// (if the ConfigState allows it).
func canSortSimply(kind reflect.Kind) bool {
// This switch parallels valueSortLess, except for the default case.
switch kind {
case reflect.Bool:
return true
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.String:
return true
case reflect.Uintptr:
return true
case reflect.Array:
return true
}
return false
}
// Len returns the number of values in the slice. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Len() int {
return len(s.values)
}
// Swap swaps the values at the passed indices. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
if s.strings != nil {
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
}
}
// valueSortLess returns whether the first value should sort before the second
// value. It is used by valueSorter.Less as part of the sort.Interface
// implementation.
func valueSortLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Bool:
return !a.Bool() && b.Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return a.Int() < b.Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
case reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Array:
// Compare the contents of both arrays.
l := a.Len()
for i := 0; i < l; i++ {
av := a.Index(i)
bv := b.Index(i)
if av.Interface() == bv.Interface() {
continue
}
return valueSortLess(av, bv)
}
}
return a.String() < b.String()
}
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
if s.strings == nil {
return valueSortLess(s.values[i], s.values[j])
}
return s.strings[i] < s.strings[j]
}
// sortValues is a sort function that handles both native types and any type that
// can be converted to error or Stringer. Other inputs are sorted according to
// their Value.String() value to ensure display stability.
func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
sort.Sort(newValuesSorter(values, cs))
}
-298
View File
@@ -1,298 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew_test
import (
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// custom type to test Stinger interface on non-pointer receiver.
type stringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with non-pointer receivers.
func (s stringer) String() string {
return "stringer " + string(s)
}
// custom type to test Stinger interface on pointer receiver.
type pstringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with only pointer receivers.
func (s *pstringer) String() string {
return "stringer " + string(*s)
}
// xref1 and xref2 are cross referencing structs for testing circular reference
// detection.
type xref1 struct {
ps2 *xref2
}
type xref2 struct {
ps1 *xref1
}
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
// reference for testing detection.
type indirCir1 struct {
ps2 *indirCir2
}
type indirCir2 struct {
ps3 *indirCir3
}
type indirCir3 struct {
ps1 *indirCir1
}
// embed is used to test embedded structures.
type embed struct {
a string
}
// embedwrap is used to test embedded structures.
type embedwrap struct {
*embed
e *embed
}
// panicer is used to intentionally cause a panic for testing spew properly
// handles them
type panicer int
func (p panicer) String() string {
panic("test panic")
}
// customError is used to test custom error interface invocation.
type customError int
func (e customError) Error() string {
return fmt.Sprintf("error: %d", int(e))
}
// stringizeWants converts a slice of wanted test output into a format suitable
// for a test error message.
func stringizeWants(wants []string) string {
s := ""
for i, want := range wants {
if i > 0 {
s += fmt.Sprintf("want%d: %s", i+1, want)
} else {
s += "want: " + want
}
}
return s
}
// testFailed returns whether or not a test failed by checking if the result
// of the test is in the slice of wanted strings.
func testFailed(result string, wants []string) bool {
for _, want := range wants {
if result == want {
return false
}
}
return true
}
type sortableStruct struct {
x int
}
func (ss sortableStruct) String() string {
return fmt.Sprintf("ss.%d", ss.x)
}
type unsortableStruct struct {
x int
}
type sortTestCase struct {
input []reflect.Value
expected []reflect.Value
}
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
getInterfaces := func(values []reflect.Value) []interface{} {
interfaces := []interface{}{}
for _, v := range values {
interfaces = append(interfaces, v.Interface())
}
return interfaces
}
for _, test := range tests {
spew.SortValues(test.input, cs)
// reflect.DeepEqual cannot really make sense of reflect.Value,
// probably because of all the pointer tricks. For instance,
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
// instead.
input := getInterfaces(test.input)
expected := getInterfaces(test.expected)
if !reflect.DeepEqual(input, expected) {
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
}
}
}
// TestSortValues ensures the sort functionality for relect.Value based sorting
// works as intended.
func TestSortValues(t *testing.T) {
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
embedA := v(embed{"a"})
embedB := v(embed{"b"})
embedC := v(embed{"c"})
tests := []sortTestCase{
// No values.
{
[]reflect.Value{},
[]reflect.Value{},
},
// Bools.
{
[]reflect.Value{v(false), v(true), v(false)},
[]reflect.Value{v(false), v(false), v(true)},
},
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Uints.
{
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
},
// Floats.
{
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// Array
{
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
},
// Uintptrs.
{
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
},
// SortableStructs.
{
// Note: not sorted - DisableMethods is set.
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
// Note: not sorted - SpewKeys is false.
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
},
// Invalid.
{
[]reflect.Value{embedB, embedA, embedC},
[]reflect.Value{embedB, embedA, embedC},
},
}
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
helpTestSortValues(tests, &cs, t)
}
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
// based sorting works as intended when using string methods.
func TestSortValuesWithMethods(t *testing.T) {
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
tests := []sortTestCase{
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// SortableStructs.
{
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
// Note: not sorted - SpewKeys is false.
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
},
}
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
helpTestSortValues(tests, &cs, t)
}
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
// based sorting works as intended when using spew to stringify keys.
func TestSortValuesWithSpew(t *testing.T) {
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
tests := []sortTestCase{
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// SortableStructs.
{
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
},
}
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
helpTestSortValues(tests, &cs, t)
}
-306
View File
@@ -1,306 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew
import (
"bytes"
"fmt"
"io"
"os"
)
// ConfigState houses the configuration options used by spew to format and
// display values. There is a global instance, Config, that is used to control
// all top-level Formatter and Dump functionality. Each ConfigState instance
// provides methods equivalent to the top-level functions.
//
// The zero value for ConfigState provides no indentation. You would typically
// want to set it to a space or a tab.
//
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
// with default settings. See the documentation of NewDefaultConfig for default
// values.
type ConfigState struct {
// Indent specifies the string to use for each indentation level. The
// global config instance that all top-level functions use set this to a
// single space by default. If you would like more indentation, you might
// set this to a tab with "\t" or perhaps two spaces with " ".
Indent string
// MaxDepth controls the maximum number of levels to descend into nested
// data structures. The default, 0, means there is no limit.
//
// NOTE: Circular data structures are properly detected, so it is not
// necessary to set this value unless you specifically want to limit deeply
// nested data structures.
MaxDepth int
// DisableMethods specifies whether or not error and Stringer interfaces are
// invoked for types that implement them.
DisableMethods bool
// DisablePointerMethods specifies whether or not to check for and invoke
// error and Stringer interfaces on types which only accept a pointer
// receiver when the current type is not a pointer.
//
// NOTE: This might be an unsafe action since calling one of these methods
// with a pointer receiver could technically mutate the value, however,
// in practice, types which choose to satisify an error or Stringer
// interface with a pointer receiver should not be mutating their state
// inside these interface methods. As a result, this option relies on
// access to the unsafe package, so it will not have any effect when
// running in environments without access to the unsafe package such as
// Google App Engine or with the "safe" build tag specified.
DisablePointerMethods bool
// DisablePointerAddresses specifies whether to disable the printing of
// pointer addresses. This is useful when diffing data structures in tests.
DisablePointerAddresses bool
// DisableCapacities specifies whether to disable the printing of capacities
// for arrays, slices, maps and channels. This is useful when diffing
// data structures in tests.
DisableCapacities bool
// ContinueOnMethod specifies whether or not recursion should continue once
// a custom error or Stringer interface is invoked. The default, false,
// means it will print the results of invoking the custom error or Stringer
// interface and return immediately instead of continuing to recurse into
// the internals of the data type.
//
// NOTE: This flag does not have any effect if method invocation is disabled
// via the DisableMethods or DisablePointerMethods options.
ContinueOnMethod bool
// SortKeys specifies map keys should be sorted before being printed. Use
// this to have a more deterministic, diffable output. Note that only
// native types (bool, int, uint, floats, uintptr and string) and types
// that support the error or Stringer interfaces (if methods are
// enabled) are supported, with other types sorted according to the
// reflect.Value.String() output which guarantees display stability.
SortKeys bool
// SpewKeys specifies that, as a last resort attempt, map keys should
// be spewed to strings and sorted by those strings. This is only
// considered if SortKeys is true.
SpewKeys bool
}
// Config is the active configuration of the top-level functions.
// The configuration can be changed by modifying the contents of spew.Config.
var Config = ConfigState{Indent: " "}
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the formatted string as a value that satisfies error. See NewFormatter
// for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, c.convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, c.convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, c.convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a Formatter interface returned by c.NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, c.convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
return fmt.Print(c.convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, c.convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
return fmt.Println(c.convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprint(a ...interface{}) string {
return fmt.Sprint(c.convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a Formatter interface returned by c.NewFormatter. It returns
// the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, c.convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a Formatter interface returned by c.NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
func (c *ConfigState) Sprintln(a ...interface{}) string {
return fmt.Sprintln(c.convertArgs(a)...)
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
c.Printf, c.Println, or c.Printf.
*/
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(c, v)
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
fdump(c, w, a...)
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by modifying the public members
of c. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func (c *ConfigState) Dump(a ...interface{}) {
fdump(c, os.Stdout, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func (c *ConfigState) Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(c, &buf, a...)
return buf.String()
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a spew Formatter interface using
// the ConfigState associated with s.
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = newFormatter(c, arg)
}
return formatters
}
// NewDefaultConfig returns a ConfigState with the following default settings.
//
// Indent: " "
// MaxDepth: 0
// DisableMethods: false
// DisablePointerMethods: false
// ContinueOnMethod: false
// SortKeys: false
func NewDefaultConfig() *ConfigState {
return &ConfigState{Indent: " "}
}
-211
View File
@@ -1,211 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.
A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output (only when using
Dump style)
There are two different approaches spew allows for dumping Go data structures:
* Dump style which prints with newlines, customizable indentation,
and additional debug information such as types and all pointer addresses
used to indirect to the final value
* A custom Formatter interface that integrates cleanly with the standard fmt
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
similar to the default %v while providing the additional functionality
outlined above and passing unsupported format verbs such as %x and %q
along to fmt
Quick Start
This section demonstrates how to quickly get started with spew. See the
sections below for further details on formatting and configuration options.
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
spew.Dump(myVar1, myVar2, ...)
spew.Fdump(someWriter, myVar1, myVar2, ...)
str := spew.Sdump(myVar1, myVar2, ...)
Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
Configuration Options
Configuration of spew is handled by fields in the ConfigState type. For
convenience, all of the top-level functions use a global state available
via the spew.Config global.
It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions. This allows concurrent configuration
options. See the ConfigState documentation for more details.
The following configuration options are available:
* Indent
String to use for each indentation level for Dump functions.
It is a single space by default. A popular alternative is "\t".
* MaxDepth
Maximum number of levels to descend into nested data structures.
There is no limit by default.
* DisableMethods
Disables invocation of error and Stringer interface methods.
Method invocation is enabled by default.
* DisablePointerMethods
Disables invocation of error and Stringer interface methods on types
which only accept pointer receivers from non-pointer variables.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of
capacities for arrays, slices, maps and channels. This is useful when
diffing data structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are
supported with other types sorted according to the
reflect.Value.String() output which guarantees display
stability. Natural map order is used by default.
* SpewKeys
Specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only
considered if SortKeys is true.
Dump Usage
Simply call spew.Dump with a list of variables you want to dump:
spew.Dump(myVar1, myVar2, ...)
You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer. For example, to dump to standard error:
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
A third option is to call spew.Sdump to get the formatted output as a string:
str := spew.Sdump(myVar1, myVar2, ...)
Sample Dump Output
See the Dump example for details on the setup of the types and variables being
shown here.
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) (len=1) {
(string) (len=3) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) (len=32 cap=32) {
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
00000020 31 32 |12|
}
Custom Formatter
Spew provides a custom formatter that implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Custom Formatter Usage
The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
functions have syntax you are most likely already familiar with:
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
spew.Println(myVar, myVar2)
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
See the Index for the full list convenience functions.
Sample Formatter Output
Double pointer to a uint8:
%v: <**>5
%+v: <**>(0xf8400420d0->0xf8400420c8)5
%#v: (**uint8)5
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
Pointer to circular struct with a uint8 field and a pointer to itself:
%v: <*>{1 <*><shown>}
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
See the Printf example for details on the setup of variables being shown
here.
Errors
Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output. Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew
-509
View File
@@ -1,509 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
// uint8Type is a reflect.Type representing a uint8. It is used to
// convert cgo types to uint8 slices for hexdumping.
uint8Type = reflect.TypeOf(uint8(0))
// cCharRE is a regular expression that matches a cgo char.
// It is used to detect character arrays to hexdump them.
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
// char. It is used to detect unsigned character arrays to hexdump
// them.
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
// It is used to detect uint8_t arrays to hexdump them.
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
)
// dumpState contains information about the state of a dump operation.
type dumpState struct {
w io.Writer
depth int
pointers map[uintptr]int
ignoreNextType bool
ignoreNextIndent bool
cs *ConfigState
}
// indent performs indentation according to the depth level and cs.Indent
// option.
func (d *dumpState) indent() {
if d.ignoreNextIndent {
d.ignoreNextIndent = false
return
}
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
}
// unpackValue returns values inside of non-nil interfaces when possible.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
// dumpPtr handles formatting of pointers by indirecting them as necessary.
func (d *dumpState) dumpPtr(v reflect.Value) {
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range d.pointers {
if depth >= d.depth {
delete(d.pointers, k)
}
}
// Keep list of all dereferenced pointers to show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by dereferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
cycleFound = true
indirects--
break
}
d.pointers[addr] = d.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type information.
d.w.Write(openParenBytes)
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
d.w.Write([]byte(ve.Type().String()))
d.w.Write(closeParenBytes)
// Display pointer information.
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
d.w.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
d.w.Write(pointerChainBytes)
}
printHexPtr(d.w, addr)
}
d.w.Write(closeParenBytes)
}
// Display dereferenced value.
d.w.Write(openParenBytes)
switch {
case nilFound:
d.w.Write(nilAngleBytes)
case cycleFound:
d.w.Write(circularBytes)
default:
d.ignoreNextType = true
d.dump(ve)
}
d.w.Write(closeParenBytes)
}
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
// reflection) arrays and slices are dumped in hexdump -C fashion.
func (d *dumpState) dumpSlice(v reflect.Value) {
// Determine whether this type should be hex dumped or not. Also,
// for types which should be hexdumped, try to use the underlying data
// first, then fall back to trying to convert them to a uint8 slice.
var buf []uint8
doConvert := false
doHexDump := false
numEntries := v.Len()
if numEntries > 0 {
vt := v.Index(0).Type()
vts := vt.String()
switch {
// C types that need to be converted.
case cCharRE.MatchString(vts):
fallthrough
case cUnsignedCharRE.MatchString(vts):
fallthrough
case cUint8tCharRE.MatchString(vts):
doConvert = true
// Try to use existing uint8 slices and fall back to converting
// and copying if that fails.
case vt.Kind() == reflect.Uint8:
// We need an addressable interface to convert the type
// to a byte slice. However, the reflect package won't
// give us an interface on certain things like
// unexported struct fields in order to enforce
// visibility rules. We use unsafe, when available, to
// bypass these restrictions since this package does not
// mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
if !UnsafeDisabled {
vs = vs.Slice(0, numEntries)
// Use the existing uint8 slice if it can be
// type asserted.
iface := vs.Interface()
if slice, ok := iface.([]uint8); ok {
buf = slice
doHexDump = true
break
}
}
// The underlying data needs to be converted if it can't
// be type asserted to a uint8 slice.
doConvert = true
}
// Copy and convert the underlying type if needed.
if doConvert && vt.ConvertibleTo(uint8Type) {
// Convert and copy each element into a uint8 byte
// slice.
buf = make([]uint8, numEntries)
for i := 0; i < numEntries; i++ {
vv := v.Index(i)
buf[i] = uint8(vv.Convert(uint8Type).Uint())
}
doHexDump = true
}
}
// Hexdump the entire slice as needed.
if doHexDump {
indent := strings.Repeat(d.cs.Indent, d.depth)
str := indent + hex.Dump(buf)
str = strings.Replace(str, "\n", "\n"+indent, -1)
str = strings.TrimRight(str, d.cs.Indent)
d.w.Write([]byte(str))
return
}
// Recursively call dump for each item.
for i := 0; i < numEntries; i++ {
d.dump(d.unpackValue(v.Index(i)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
// dump is the main workhorse for dumping a value. It uses the passed reflect
// value to figure out what kind of object we are dealing with and formats it
// appropriately. It is a recursive function, however circular data structures
// are detected and handled properly.
func (d *dumpState) dump(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
d.w.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
d.indent()
d.dumpPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !d.ignoreNextType {
d.indent()
d.w.Write(openParenBytes)
d.w.Write([]byte(v.Type().String()))
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
d.ignoreNextType = false
// Display length and capacity if the built-in len and cap functions
// work with the value's kind and the len/cap itself is non-zero.
valueLen, valueCap := 0, 0
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Chan:
valueLen, valueCap = v.Len(), v.Cap()
case reflect.Map, reflect.String:
valueLen = v.Len()
}
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
d.w.Write(openParenBytes)
if valueLen != 0 {
d.w.Write(lenEqualsBytes)
printInt(d.w, int64(valueLen), 10)
}
if !d.cs.DisableCapacities && valueCap != 0 {
if valueLen != 0 {
d.w.Write(spaceBytes)
}
d.w.Write(capEqualsBytes)
printInt(d.w, int64(valueCap), 10)
}
d.w.Write(closeParenBytes)
d.w.Write(spaceBytes)
}
// Call Stringer/error interfaces if they exist and the handle methods flag
// is enabled
if !d.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(d.cs, d.w, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(d.w, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(d.w, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(d.w, v.Uint(), 10)
case reflect.Float32:
printFloat(d.w, v.Float(), 32)
case reflect.Float64:
printFloat(d.w, v.Float(), 64)
case reflect.Complex64:
printComplex(d.w, v.Complex(), 32)
case reflect.Complex128:
printComplex(d.w, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
d.dumpSlice(v)
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.String:
d.w.Write([]byte(strconv.Quote(v.String())))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
d.w.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
d.w.Write(nilAngleBytes)
break
}
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
numEntries := v.Len()
keys := v.MapKeys()
if d.cs.SortKeys {
sortValues(keys, d.cs)
}
for i, key := range keys {
d.dump(d.unpackValue(key))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.MapIndex(key)))
if i < (numEntries - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Struct:
d.w.Write(openBraceNewlineBytes)
d.depth++
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
d.indent()
d.w.Write(maxNewlineBytes)
} else {
vt := v.Type()
numFields := v.NumField()
for i := 0; i < numFields; i++ {
d.indent()
vtf := vt.Field(i)
d.w.Write([]byte(vtf.Name))
d.w.Write(colonSpaceBytes)
d.ignoreNextIndent = true
d.dump(d.unpackValue(v.Field(i)))
if i < (numFields - 1) {
d.w.Write(commaNewlineBytes)
} else {
d.w.Write(newlineBytes)
}
}
}
d.depth--
d.indent()
d.w.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(d.w, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(d.w, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it in case any new
// types are added.
default:
if v.CanInterface() {
fmt.Fprintf(d.w, "%v", v.Interface())
} else {
fmt.Fprintf(d.w, "%v", v.String())
}
}
}
// fdump is a helper function to consolidate the logic from the various public
// methods which take varying writers and config states.
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
for _, arg := range a {
if arg == nil {
w.Write(interfaceBytes)
w.Write(spaceBytes)
w.Write(nilAngleBytes)
w.Write(newlineBytes)
continue
}
d := dumpState{w: w, cs: cs}
d.pointers = make(map[uintptr]int)
d.dump(reflect.ValueOf(arg))
d.w.Write(newlineBytes)
}
}
// Fdump formats and displays the passed arguments to io.Writer w. It formats
// exactly the same as Dump.
func Fdump(w io.Writer, a ...interface{}) {
fdump(&Config, w, a...)
}
// Sdump returns a string with the passed arguments formatted exactly the same
// as Dump.
func Sdump(a ...interface{}) string {
var buf bytes.Buffer
fdump(&Config, &buf, a...)
return buf.String()
}
/*
Dump displays the passed parameters to standard out with newlines, customizable
indentation, and additional debug information such as complete types and all
pointer addresses used to indirect to the final value. It provides the
following features over the built-in printing facilities provided by the fmt
package:
* Pointers are dereferenced and followed
* Circular data structures are detected and handled properly
* Custom Stringer/error interfaces are optionally invoked, including
on unexported types
* Custom types which only implement the Stringer/error interfaces via
a pointer receiver are optionally invoked when passing non-pointer
variables
* Byte arrays and slices are dumped like the hexdump -C command which
includes offsets, byte values in hex, and ASCII output
The configuration options are controlled by an exported package global,
spew.Config. See ConfigState for options documentation.
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
get the formatted result as a string.
*/
func Dump(a ...interface{}) {
fdump(&Config, os.Stdout, a...)
}
File diff suppressed because it is too large Load Diff
-101
View File
@@ -1,101 +0,0 @@
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and 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.
// NOTE: Due to the following build constraints, this file will only be compiled
// when both cgo is supported and "-tags testcgo" is added to the go test
// command line. This means the cgo tests are only added (and hence run) when
// specifially requested. This configuration is used because spew itself
// does not require cgo to run even though it does handle certain cgo types
// specially. Rather than forcing all clients to require cgo and an external
// C compiler just to run the tests, this scheme makes them optional.
// +build cgo,testcgo
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew/testdata"
)
func addCgoDumpTests() {
// C char pointer.
v := testdata.GetCgoCharPointer()
nv := testdata.GetCgoNullCharPointer()
pv := &v
vcAddr := fmt.Sprintf("%p", v)
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "*testdata._Ctype_char"
vs := "116"
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(nv, "("+vt+")(<nil>)\n")
// C char array.
v2, v2l, v2c := testdata.GetCgoCharArray()
v2Len := fmt.Sprintf("%d", v2l)
v2Cap := fmt.Sprintf("%d", v2c)
v2t := "[6]testdata._Ctype_char"
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
"{\n 00000000 74 65 73 74 32 00 " +
" |test2.|\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
// C unsigned char array.
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
v3Len := fmt.Sprintf("%d", v3l)
v3Cap := fmt.Sprintf("%d", v3c)
v3t := "[6]testdata._Ctype_unsignedchar"
v3t2 := "[6]testdata._Ctype_uchar"
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
"{\n 00000000 74 65 73 74 33 00 " +
" |test3.|\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
// C signed char array.
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
v4Len := fmt.Sprintf("%d", v4l)
v4Cap := fmt.Sprintf("%d", v4c)
v4t := "[6]testdata._Ctype_schar"
v4t2 := "testdata._Ctype_schar"
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
") 0\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
// C uint8_t array.
v5, v5l, v5c := testdata.GetCgoUint8tArray()
v5Len := fmt.Sprintf("%d", v5l)
v5Cap := fmt.Sprintf("%d", v5c)
v5t := "[6]testdata._Ctype_uint8_t"
v5t2 := "[6]testdata._Ctype_uchar"
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
"{\n 00000000 74 65 73 74 35 00 " +
" |test5.|\n}"
addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n")
// C typedefed unsigned char array.
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
v6Len := fmt.Sprintf("%d", v6l)
v6Cap := fmt.Sprintf("%d", v6c)
v6t := "[6]testdata._Ctype_custom_uchar_t"
v6t2 := "[6]testdata._Ctype_uchar"
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
"{\n 00000000 74 65 73 74 36 00 " +
" |test6.|\n}"
addDumpTest(v6, "("+v6t+") "+v6s+"\n", "("+v6t2+") "+v6s+"\n")
}
-26
View File
@@ -1,26 +0,0 @@
// Copyright (c) 2013 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and 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.
// NOTE: Due to the following build constraints, this file will only be compiled
// when either cgo is not supported or "-tags testcgo" is not added to the go
// test command line. This file intentionally does not setup any cgo tests in
// this scenario.
// +build !cgo !testcgo
package spew_test
func addCgoDumpTests() {
// Don't add any tests for cgo since this file is only compiled when
// there should not be any cgo tests.
}
-226
View File
@@ -1,226 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew"
)
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
// This example demonstrates how to use Dump to dump variables to stdout.
func ExampleDump() {
// The following package level declarations are assumed for this example:
/*
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
*/
// Setup some sample data structures for the example.
bar := Bar{uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
f := Flag(5)
b := []byte{
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32,
}
// Dump!
spew.Dump(s1, f, b)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Flag) Unknown flag (5)
// ([]uint8) (len=34 cap=34) {
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
// 00000020 31 32 |12|
// }
//
}
// This example demonstrates how to use Printf to display a variable with a
// format string and inline formatting.
func ExamplePrintf() {
// Create a double pointer to a uint 8.
ui8 := uint8(5)
pui8 := &ui8
ppui8 := &pui8
// Create a circular data type.
type circular struct {
ui8 uint8
c *circular
}
c := circular{ui8: 1}
c.c = &c
// Print!
spew.Printf("ppui8: %v\n", ppui8)
spew.Printf("circular: %v\n", c)
// Output:
// ppui8: <**>5
// circular: {1 <*>{1 <*><shown>}}
}
// This example demonstrates how to use a ConfigState.
func ExampleConfigState() {
// Modify the indent level of the ConfigState only. The global
// configuration is not modified.
scs := spew.ConfigState{Indent: "\t"}
// Output using the ConfigState instance.
v := map[string]int{"one": 1}
scs.Printf("v: %v\n", v)
scs.Dump(v)
// Output:
// v: map[one:1]
// (map[string]int) (len=1) {
// (string) (len=3) "one": (int) 1
// }
}
// This example demonstrates how to use ConfigState.Dump to dump variables to
// stdout
func ExampleConfigState_Dump() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances with different indentation.
scs := spew.ConfigState{Indent: "\t"}
scs2 := spew.ConfigState{Indent: " "}
// Setup some sample data structures for the example.
bar := Bar{uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
// Dump using the ConfigState instances.
scs.Dump(s1)
scs2.Dump(s1)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
//
}
// This example demonstrates how to use ConfigState.Printf to display a variable
// with a format string and inline formatting.
func ExampleConfigState_Printf() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances and modify the method handling of the
// first ConfigState only.
scs := spew.NewDefaultConfig()
scs2 := spew.NewDefaultConfig()
scs.DisableMethods = true
// Alternatively
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
// scs2 := spew.ConfigState{Indent: " "}
// This is of type Flag which implements a Stringer and has raw value 1.
f := flagTwo
// Dump using the ConfigState instances.
scs.Printf("f: %v\n", f)
scs2.Printf("f: %v\n", f)
// Output:
// f: 1
// f: flagTwo
}
-419
View File
@@ -1,419 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew
import (
"bytes"
"fmt"
"reflect"
"strconv"
"strings"
)
// supportedFlags is a list of all the character flags supported by fmt package.
const supportedFlags = "0-+# "
// formatState implements the fmt.Formatter interface and contains information
// about the state of a formatting operation. The NewFormatter function can
// be used to get a new Formatter which can be used directly as arguments
// in standard fmt package printing calls.
type formatState struct {
value interface{}
fs fmt.State
depth int
pointers map[uintptr]int
ignoreNextType bool
cs *ConfigState
}
// buildDefaultFormat recreates the original format string without precision
// and width information to pass in to fmt.Sprintf in the case of an
// unrecognized type. Unless new types are added to the language, this
// function won't ever be called.
func (f *formatState) buildDefaultFormat() (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
buf.WriteRune('v')
format = buf.String()
return format
}
// constructOrigFormat recreates the original format string including precision
// and width information to pass along to the standard fmt package. This allows
// automatic deferral of all format strings this package doesn't support.
func (f *formatState) constructOrigFormat(verb rune) (format string) {
buf := bytes.NewBuffer(percentBytes)
for _, flag := range supportedFlags {
if f.fs.Flag(int(flag)) {
buf.WriteRune(flag)
}
}
if width, ok := f.fs.Width(); ok {
buf.WriteString(strconv.Itoa(width))
}
if precision, ok := f.fs.Precision(); ok {
buf.Write(precisionBytes)
buf.WriteString(strconv.Itoa(precision))
}
buf.WriteRune(verb)
format = buf.String()
return format
}
// unpackValue returns values inside of non-nil interfaces when possible and
// ensures that types for values which have been unpacked from an interface
// are displayed when the show types flag is also set.
// This is useful for data types like structs, arrays, slices, and maps which
// can contain varying types packed inside an interface.
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface {
f.ignoreNextType = false
if !v.IsNil() {
v = v.Elem()
}
}
return v
}
// formatPtr handles formatting of pointers by indirecting them as necessary.
func (f *formatState) formatPtr(v reflect.Value) {
// Display nil if top level pointer is nil.
showTypes := f.fs.Flag('#')
if v.IsNil() && (!showTypes || f.ignoreNextType) {
f.fs.Write(nilAngleBytes)
return
}
// Remove pointers at or below the current depth from map used to detect
// circular refs.
for k, depth := range f.pointers {
if depth >= f.depth {
delete(f.pointers, k)
}
}
// Keep list of all dereferenced pointers to possibly show later.
pointerChain := make([]uintptr, 0)
// Figure out how many levels of indirection there are by derferencing
// pointers and unpacking interfaces down the chain while detecting circular
// references.
nilFound := false
cycleFound := false
indirects := 0
ve := v
for ve.Kind() == reflect.Ptr {
if ve.IsNil() {
nilFound = true
break
}
indirects++
addr := ve.Pointer()
pointerChain = append(pointerChain, addr)
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
cycleFound = true
indirects--
break
}
f.pointers[addr] = f.depth
ve = ve.Elem()
if ve.Kind() == reflect.Interface {
if ve.IsNil() {
nilFound = true
break
}
ve = ve.Elem()
}
}
// Display type or indirection level depending on flags.
if showTypes && !f.ignoreNextType {
f.fs.Write(openParenBytes)
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
f.fs.Write([]byte(ve.Type().String()))
f.fs.Write(closeParenBytes)
} else {
if nilFound || cycleFound {
indirects += strings.Count(ve.Type().String(), "*")
}
f.fs.Write(openAngleBytes)
f.fs.Write([]byte(strings.Repeat("*", indirects)))
f.fs.Write(closeAngleBytes)
}
// Display pointer information depending on flags.
if f.fs.Flag('+') && (len(pointerChain) > 0) {
f.fs.Write(openParenBytes)
for i, addr := range pointerChain {
if i > 0 {
f.fs.Write(pointerChainBytes)
}
printHexPtr(f.fs, addr)
}
f.fs.Write(closeParenBytes)
}
// Display dereferenced value.
switch {
case nilFound:
f.fs.Write(nilAngleBytes)
case cycleFound:
f.fs.Write(circularShortBytes)
default:
f.ignoreNextType = true
f.format(ve)
}
}
// format is the main workhorse for providing the Formatter interface. It
// uses the passed reflect value to figure out what kind of object we are
// dealing with and formats it appropriately. It is a recursive function,
// however circular data structures are detected and handled properly.
func (f *formatState) format(v reflect.Value) {
// Handle invalid reflect values immediately.
kind := v.Kind()
if kind == reflect.Invalid {
f.fs.Write(invalidAngleBytes)
return
}
// Handle pointers specially.
if kind == reflect.Ptr {
f.formatPtr(v)
return
}
// Print type information unless already handled elsewhere.
if !f.ignoreNextType && f.fs.Flag('#') {
f.fs.Write(openParenBytes)
f.fs.Write([]byte(v.Type().String()))
f.fs.Write(closeParenBytes)
}
f.ignoreNextType = false
// Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled.
if !f.cs.DisableMethods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.cs, f.fs, v); handled {
return
}
}
}
switch kind {
case reflect.Invalid:
// Do nothing. We should never get here since invalid has already
// been handled above.
case reflect.Bool:
printBool(f.fs, v.Bool())
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
printInt(f.fs, v.Int(), 10)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
printUint(f.fs, v.Uint(), 10)
case reflect.Float32:
printFloat(f.fs, v.Float(), 32)
case reflect.Float64:
printFloat(f.fs, v.Float(), 64)
case reflect.Complex64:
printComplex(f.fs, v.Complex(), 32)
case reflect.Complex128:
printComplex(f.fs, v.Complex(), 64)
case reflect.Slice:
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
fallthrough
case reflect.Array:
f.fs.Write(openBracketBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
numEntries := v.Len()
for i := 0; i < numEntries; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(v.Index(i)))
}
}
f.depth--
f.fs.Write(closeBracketBytes)
case reflect.String:
f.fs.Write([]byte(v.String()))
case reflect.Interface:
// The only time we should get here is for nil interfaces due to
// unpackValue calls.
if v.IsNil() {
f.fs.Write(nilAngleBytes)
}
case reflect.Ptr:
// Do nothing. We should never get here since pointers have already
// been handled above.
case reflect.Map:
// nil maps should be indicated as different than empty maps
if v.IsNil() {
f.fs.Write(nilAngleBytes)
break
}
f.fs.Write(openMapBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
keys := v.MapKeys()
if f.cs.SortKeys {
sortValues(keys, f.cs)
}
for i, key := range keys {
if i > 0 {
f.fs.Write(spaceBytes)
}
f.ignoreNextType = true
f.format(f.unpackValue(key))
f.fs.Write(colonBytes)
f.ignoreNextType = true
f.format(f.unpackValue(v.MapIndex(key)))
}
}
f.depth--
f.fs.Write(closeMapBytes)
case reflect.Struct:
numFields := v.NumField()
f.fs.Write(openBraceBytes)
f.depth++
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
f.fs.Write(maxShortBytes)
} else {
vt := v.Type()
for i := 0; i < numFields; i++ {
if i > 0 {
f.fs.Write(spaceBytes)
}
vtf := vt.Field(i)
if f.fs.Flag('+') || f.fs.Flag('#') {
f.fs.Write([]byte(vtf.Name))
f.fs.Write(colonBytes)
}
f.format(f.unpackValue(v.Field(i)))
}
}
f.depth--
f.fs.Write(closeBraceBytes)
case reflect.Uintptr:
printHexPtr(f.fs, uintptr(v.Uint()))
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
printHexPtr(f.fs, v.Pointer())
// There were not any other types at the time this code was written, but
// fall back to letting the default fmt package handle it if any get added.
default:
format := f.buildDefaultFormat()
if v.CanInterface() {
fmt.Fprintf(f.fs, format, v.Interface())
} else {
fmt.Fprintf(f.fs, format, v.String())
}
}
}
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
// details.
func (f *formatState) Format(fs fmt.State, verb rune) {
f.fs = fs
// Use standard formatting for verbs that are not v.
if verb != 'v' {
format := f.constructOrigFormat(verb)
fmt.Fprintf(fs, format, f.value)
return
}
if f.value == nil {
if fs.Flag('#') {
fs.Write(interfaceBytes)
}
fs.Write(nilAngleBytes)
return
}
f.format(reflect.ValueOf(f.value))
}
// newFormatter is a helper function to consolidate the logic from the various
// public methods which take varying config states.
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
fs := &formatState{value: v, cs: cs}
fs.pointers = make(map[uintptr]int)
return fs
}
/*
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
interface. As a result, it integrates cleanly with standard fmt package
printing functions. The formatter is useful for inline printing of smaller data
types similar to the standard %v format specifier.
The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations. Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting. In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).
Typically this function shouldn't be called directly. It is much easier to make
use of the custom formatter by calling one of the convenience functions such as
Printf, Println, or Fprintf.
*/
func NewFormatter(v interface{}) fmt.Formatter {
return newFormatter(&Config, v)
}
File diff suppressed because it is too large Load Diff
-84
View File
@@ -1,84 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
/*
This test file is part of the spew package rather than than the spew_test
package because it needs access to internals to properly test certain cases
which are not possible via the public interface since they should never happen.
*/
package spew
import (
"bytes"
"reflect"
"testing"
)
// dummyFmtState implements a fake fmt.State to use for testing invalid
// reflect.Value handling. This is necessary because the fmt package catches
// invalid values before invoking the formatter on them.
type dummyFmtState struct {
bytes.Buffer
}
func (dfs *dummyFmtState) Flag(f int) bool {
return f == int('+')
}
func (dfs *dummyFmtState) Precision() (int, bool) {
return 0, false
}
func (dfs *dummyFmtState) Width() (int, bool) {
return 0, false
}
// TestInvalidReflectValue ensures the dump and formatter code handles an
// invalid reflect value properly. This needs access to internal state since it
// should never happen in real code and therefore can't be tested via the public
// API.
func TestInvalidReflectValue(t *testing.T) {
i := 1
// Dump invalid reflect value.
v := new(reflect.Value)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(*v)
s := buf.String()
want := "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter invalid reflect value.
buf2 := new(dummyFmtState)
f := formatState{value: *v, cs: &Config, fs: buf2}
f.format(*v)
s = buf2.String()
want = "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
}
}
// SortValues makes the internal sortValues function available to the test
// package.
func SortValues(values []reflect.Value, cs *ConfigState) {
sortValues(values, cs)
}
-101
View File
@@ -1,101 +0,0 @@
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
// Permission to use, copy, modify, and 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.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
/*
This test file is part of the spew package rather than than the spew_test
package because it needs access to internals to properly test certain cases
which are not possible via the public interface since they should never happen.
*/
package spew
import (
"bytes"
"reflect"
"testing"
)
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
// the maximum kind value which does not exist. This is needed to test the
// fallback code which punts to the standard fmt library for new types that
// might get added to the language.
func changeKind(v *reflect.Value, readOnly bool) {
flags := flagField(v)
if readOnly {
*flags |= flagRO
} else {
*flags &^= flagRO
}
*flags |= flagKindMask
}
// TestAddedReflectValue tests functionaly of the dump and formatter code which
// falls back to the standard fmt library for new types that might get added to
// the language.
func TestAddedReflectValue(t *testing.T) {
i := 1
// Dump using a reflect.Value that is exported.
v := reflect.ValueOf(int8(5))
changeKind(&v, false)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(v)
s := buf.String()
want := "(int8) 5"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Dump using a reflect.Value that is not exported.
changeKind(&v, true)
buf.Reset()
d.dump(v)
s = buf.String()
want = "(int8) <int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is exported.
changeKind(&v, false)
buf2 := new(dummyFmtState)
f := formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "5"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is not exported.
changeKind(&v, true)
buf2.Reset()
f = formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "<int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
}
-148
View File
@@ -1,148 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew
import (
"fmt"
"io"
)
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the formatted string as a value that satisfies error. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Errorf(format string, a ...interface{}) (err error) {
return fmt.Errorf(format, convertArgs(a)...)
}
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprint(w, convertArgs(a)...)
}
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
return fmt.Fprintf(w, format, convertArgs(a)...)
}
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
// passed with a default Formatter interface returned by NewFormatter. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
return fmt.Fprintln(w, convertArgs(a)...)
}
// Print is a wrapper for fmt.Print that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
func Print(a ...interface{}) (n int, err error) {
return fmt.Print(convertArgs(a)...)
}
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Printf(format string, a ...interface{}) (n int, err error) {
return fmt.Printf(format, convertArgs(a)...)
}
// Println is a wrapper for fmt.Println that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the number of bytes written and any write error encountered. See
// NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
func Println(a ...interface{}) (n int, err error) {
return fmt.Println(convertArgs(a)...)
}
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprint(a ...interface{}) string {
return fmt.Sprint(convertArgs(a)...)
}
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
// passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintf(format string, a ...interface{}) string {
return fmt.Sprintf(format, convertArgs(a)...)
}
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
// were passed with a default Formatter interface returned by NewFormatter. It
// returns the resulting string. See NewFormatter for formatting details.
//
// This function is shorthand for the following syntax:
//
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
func Sprintln(a ...interface{}) string {
return fmt.Sprintln(convertArgs(a)...)
}
// convertArgs accepts a slice of arguments and returns a slice of the same
// length with each argument converted to a default spew Formatter interface.
func convertArgs(args []interface{}) (formatters []interface{}) {
formatters = make([]interface{}, len(args))
for index, arg := range args {
formatters[index] = NewFormatter(arg)
}
return formatters
}
-320
View File
@@ -1,320 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and 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.
*/
package spew_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/davecgh/go-spew/spew"
)
// spewFunc is used to identify which public function of the spew package or
// ConfigState a test applies to.
type spewFunc int
const (
fCSFdump spewFunc = iota
fCSFprint
fCSFprintf
fCSFprintln
fCSPrint
fCSPrintln
fCSSdump
fCSSprint
fCSSprintf
fCSSprintln
fCSErrorf
fCSNewFormatter
fErrorf
fFprint
fFprintln
fPrint
fPrintln
fSdump
fSprint
fSprintf
fSprintln
)
// Map of spewFunc values to names for pretty printing.
var spewFuncStrings = map[spewFunc]string{
fCSFdump: "ConfigState.Fdump",
fCSFprint: "ConfigState.Fprint",
fCSFprintf: "ConfigState.Fprintf",
fCSFprintln: "ConfigState.Fprintln",
fCSSdump: "ConfigState.Sdump",
fCSPrint: "ConfigState.Print",
fCSPrintln: "ConfigState.Println",
fCSSprint: "ConfigState.Sprint",
fCSSprintf: "ConfigState.Sprintf",
fCSSprintln: "ConfigState.Sprintln",
fCSErrorf: "ConfigState.Errorf",
fCSNewFormatter: "ConfigState.NewFormatter",
fErrorf: "spew.Errorf",
fFprint: "spew.Fprint",
fFprintln: "spew.Fprintln",
fPrint: "spew.Print",
fPrintln: "spew.Println",
fSdump: "spew.Sdump",
fSprint: "spew.Sprint",
fSprintf: "spew.Sprintf",
fSprintln: "spew.Sprintln",
}
func (f spewFunc) String() string {
if s, ok := spewFuncStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
}
// spewTest is used to describe a test to be performed against the public
// functions of the spew package or ConfigState.
type spewTest struct {
cs *spew.ConfigState
f spewFunc
format string
in interface{}
want string
}
// spewTests houses the tests to be performed against the public functions of
// the spew package and ConfigState.
//
// These tests are only intended to ensure the public functions are exercised
// and are intentionally not exhaustive of types. The exhaustive type
// tests are handled in the dump and format tests.
var spewTests []spewTest
// redirStdout is a helper function to return the standard output from f as a
// byte slice.
func redirStdout(f func()) ([]byte, error) {
tempFile, err := ioutil.TempFile("", "ss-test")
if err != nil {
return nil, err
}
fileName := tempFile.Name()
defer os.Remove(fileName) // Ignore error
origStdout := os.Stdout
os.Stdout = tempFile
f()
os.Stdout = origStdout
tempFile.Close()
return ioutil.ReadFile(fileName)
}
func initSpewTests() {
// Config states with various settings.
scsDefault := spew.NewDefaultConfig()
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
scsNoCap := &spew.ConfigState{DisableCapacities: true}
// Variables for tests on types which implement Stringer interface with and
// without a pointer receiver.
ts := stringer("test")
tps := pstringer("test")
type ptrTester struct {
s *struct{}
}
tptr := &ptrTester{s: &struct{}{}}
// depthTester is used to test max depth handling for structs, array, slices
// and maps.
type depthTester struct {
ic indirCir1
arr [1]string
slice []string
m map[string]int
}
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
map[string]int{"one": 1}}
// Variable for tests on types which implement error interface.
te := customError(10)
spewTests = []spewTest{
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
{scsDefault, fCSFprint, "", int16(32767), "32767"},
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
{scsDefault, fFprint, "", float32(3.14), "3.14"},
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
{scsDefault, fPrint, "", true, "true"},
{scsDefault, fPrintln, "", false, "false\n"},
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
{scsNoMethods, fCSFprint, "", ts, "test"},
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
{scsNoMethods, fCSFprint, "", tps, "test"},
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
{scsNoPmethods, fCSFprint, "", tps, "test"},
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
"(len=4) (stringer test) \"test\"\n"},
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
"(error: 10) 10\n"},
{scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"},
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\n"},
}
}
// TestSpew executes all of the tests described by spewTests.
func TestSpew(t *testing.T) {
initSpewTests()
t.Logf("Running %d tests", len(spewTests))
for i, test := range spewTests {
buf := new(bytes.Buffer)
switch test.f {
case fCSFdump:
test.cs.Fdump(buf, test.in)
case fCSFprint:
test.cs.Fprint(buf, test.in)
case fCSFprintf:
test.cs.Fprintf(buf, test.format, test.in)
case fCSFprintln:
test.cs.Fprintln(buf, test.in)
case fCSPrint:
b, err := redirStdout(func() { test.cs.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSPrintln:
b, err := redirStdout(func() { test.cs.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSSdump:
str := test.cs.Sdump(test.in)
buf.WriteString(str)
case fCSSprint:
str := test.cs.Sprint(test.in)
buf.WriteString(str)
case fCSSprintf:
str := test.cs.Sprintf(test.format, test.in)
buf.WriteString(str)
case fCSSprintln:
str := test.cs.Sprintln(test.in)
buf.WriteString(str)
case fCSErrorf:
err := test.cs.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fCSNewFormatter:
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
case fErrorf:
err := spew.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fFprint:
spew.Fprint(buf, test.in)
case fFprintln:
spew.Fprintln(buf, test.in)
case fPrint:
b, err := redirStdout(func() { spew.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fPrintln:
b, err := redirStdout(func() { spew.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fSdump:
str := spew.Sdump(test.in)
buf.WriteString(str)
case fSprint:
str := spew.Sprint(test.in)
buf.WriteString(str)
case fSprintf:
str := spew.Sprintf(test.format, test.in)
buf.WriteString(str)
case fSprintln:
str := spew.Sprintln(test.in)
buf.WriteString(str)
default:
t.Errorf("%v #%d unrecognized function", test.f, i)
continue
}
s := buf.String()
if test.want != s {
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
continue
}
}
}
-61
View File
@@ -1,61 +0,0 @@
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
-5
View File
@@ -1,5 +0,0 @@
language: go
go:
- 1.8.x
- tip
-27
View File
@@ -1,27 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
solver-name = "gps-cdcl"
solver-version = 1
-30
View File
@@ -1,30 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/mattn/go-colorable"
version = "0.0.9"
[[constraint]]
name = "github.com/mattn/go-isatty"
version = "0.0.3"
-20
View File
@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Fatih Arslan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-179
View File
@@ -1,179 +0,0 @@
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
Color lets you use colorized outputs in terms of [ANSI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
has support for Windows too! The API can be used in several ways, pick one that
suits you.
![Color](https://i.imgur.com/c1JI0lA.png)
## Install
```bash
go get github.com/fatih/color
```
Note that the `vendor` folder is here for stability. Remove the folder if you
already have the dependencies in your GOPATH.
## Examples
### Standard colors
```go
// Print with default helper functions
color.Cyan("Prints text in cyan.")
// A newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// These are using the default foreground colors
color.Red("We have red")
color.Magenta("And many others ..")
```
### Mix and reuse colors
```go
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with white background.")
```
### Use your own output (io.Writer)
```go
// Use your own io.Writer output
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
blue := color.New(color.FgBlue)
blue.Fprint(writer, "This will print text in blue.")
```
### Custom print functions (PrintFunc)
```go
// Create a custom print function for convenience
red := color.New(color.FgRed).PrintfFunc()
red("Warning")
red("Error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("Don't forget this...")
```
### Custom fprint functions (FprintFunc)
```go
blue := color.New(FgBlue).FprintfFunc()
blue(myWriter, "important notice: %s", stars)
// Mix up with multiple attributes
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
success(myWriter, "Don't forget this...")
```
### Insert into noncolor strings (SprintFunc)
```go
// Create SprintXxx functions to mix strings with other non-colorized strings:
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
fmt.Printf("This %s rocks!\n", info("package"))
// Use helper functions
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
// Windows supported too! Just don't forget to change the output to color.Output
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
```
### Plug into existing code
```go
// Use handy standard colors
color.Set(color.FgYellow)
fmt.Println("Existing text will now be in yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // Don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // Use it in your function
fmt.Println("All text will now be bold magenta.")
```
### Disable/Enable color
There might be a case where you want to explicitly disable/enable color output. the
`go-isatty` package will automatically disable color output for non-tty output streams
(for example if the output were piped directly to `less`)
`Color` has support to disable/enable colors both globally and for single color
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
can easily disable the color output with:
```go
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
```
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
```go
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")
```
## Todo
* Save/Return previous values
* Evaluate fmt.Formatter interface
## Credits
* [Fatih Arslan](https://github.com/fatih)
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
## License
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
-603
View File
@@ -1,603 +0,0 @@
package color
import (
"fmt"
"io"
"os"
"strconv"
"strings"
"sync"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
var (
// NoColor defines if the output is colorized or not. It's dynamically set to
// false or true based on the stdout's file descriptor referring to a terminal
// or not. This is a global option and affects all colors. For more control
// over each color block use the methods DisableColor() individually.
NoColor = os.Getenv("TERM") == "dumb" ||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
// Output defines the standard output of the print functions. By default
// os.Stdout is used.
Output = colorable.NewColorableStdout()
// Error defines a color supporting writer for os.Stderr.
Error = colorable.NewColorableStderr()
// colorsCache is used to reduce the count of created Color objects and
// allows to reuse already created objects with required Attribute.
colorsCache = make(map[Attribute]*Color)
colorsCacheMu sync.Mutex // protects colorsCache
)
// Color defines a custom color object which is defined by SGR parameters.
type Color struct {
params []Attribute
noColor *bool
}
// Attribute defines a single SGR Code
type Attribute int
const escape = "\x1b"
// Base attributes
const (
Reset Attribute = iota
Bold
Faint
Italic
Underline
BlinkSlow
BlinkRapid
ReverseVideo
Concealed
CrossedOut
)
// Foreground text colors
const (
FgBlack Attribute = iota + 30
FgRed
FgGreen
FgYellow
FgBlue
FgMagenta
FgCyan
FgWhite
)
// Foreground Hi-Intensity text colors
const (
FgHiBlack Attribute = iota + 90
FgHiRed
FgHiGreen
FgHiYellow
FgHiBlue
FgHiMagenta
FgHiCyan
FgHiWhite
)
// Background text colors
const (
BgBlack Attribute = iota + 40
BgRed
BgGreen
BgYellow
BgBlue
BgMagenta
BgCyan
BgWhite
)
// Background Hi-Intensity text colors
const (
BgHiBlack Attribute = iota + 100
BgHiRed
BgHiGreen
BgHiYellow
BgHiBlue
BgHiMagenta
BgHiCyan
BgHiWhite
)
// New returns a newly created color object.
func New(value ...Attribute) *Color {
c := &Color{params: make([]Attribute, 0)}
c.Add(value...)
return c
}
// Set sets the given parameters immediately. It will change the color of
// output with the given SGR parameters until color.Unset() is called.
func Set(p ...Attribute) *Color {
c := New(p...)
c.Set()
return c
}
// Unset resets all escape attributes and clears the output. Usually should
// be called after Set().
func Unset() {
if NoColor {
return
}
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
}
// Set sets the SGR sequence.
func (c *Color) Set() *Color {
if c.isNoColorSet() {
return c
}
fmt.Fprintf(Output, c.format())
return c
}
func (c *Color) unset() {
if c.isNoColorSet() {
return
}
Unset()
}
func (c *Color) setWriter(w io.Writer) *Color {
if c.isNoColorSet() {
return c
}
fmt.Fprintf(w, c.format())
return c
}
func (c *Color) unsetWriter(w io.Writer) {
if c.isNoColorSet() {
return
}
if NoColor {
return
}
fmt.Fprintf(w, "%s[%dm", escape, Reset)
}
// Add is used to chain SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
func (c *Color) Add(value ...Attribute) *Color {
c.params = append(c.params, value...)
return c
}
func (c *Color) prepend(value Attribute) {
c.params = append(c.params, 0)
copy(c.params[1:], c.params[0:])
c.params[0] = value
}
// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
// On Windows, users should wrap w with colorable.NewColorable() if w is of
// type *os.File.
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
c.setWriter(w)
defer c.unsetWriter(w)
return fmt.Fprint(w, a...)
}
// Print formats using the default formats for its operands and writes to
// standard output. Spaces are added between operands when neither is a
// string. It returns the number of bytes written and any write error
// encountered. This is the standard fmt.Print() method wrapped with the given
// color.
func (c *Color) Print(a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprint(Output, a...)
}
// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
// On Windows, users should wrap w with colorable.NewColorable() if w is of
// type *os.File.
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
c.setWriter(w)
defer c.unsetWriter(w)
return fmt.Fprintf(w, format, a...)
}
// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
// This is the standard fmt.Printf() method wrapped with the given color.
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprintf(Output, format, a...)
}
// Fprintln formats using the default formats for its operands and writes to w.
// Spaces are always added between operands and a newline is appended.
// On Windows, users should wrap w with colorable.NewColorable() if w is of
// type *os.File.
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
c.setWriter(w)
defer c.unsetWriter(w)
return fmt.Fprintln(w, a...)
}
// Println formats using the default formats for its operands and writes to
// standard output. Spaces are always added between operands and a newline is
// appended. It returns the number of bytes written and any write error
// encountered. This is the standard fmt.Print() method wrapped with the given
// color.
func (c *Color) Println(a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprintln(Output, a...)
}
// Sprint is just like Print, but returns a string instead of printing it.
func (c *Color) Sprint(a ...interface{}) string {
return c.wrap(fmt.Sprint(a...))
}
// Sprintln is just like Println, but returns a string instead of printing it.
func (c *Color) Sprintln(a ...interface{}) string {
return c.wrap(fmt.Sprintln(a...))
}
// Sprintf is just like Printf, but returns a string instead of printing it.
func (c *Color) Sprintf(format string, a ...interface{}) string {
return c.wrap(fmt.Sprintf(format, a...))
}
// FprintFunc returns a new function that prints the passed arguments as
// colorized with color.Fprint().
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
return func(w io.Writer, a ...interface{}) {
c.Fprint(w, a...)
}
}
// PrintFunc returns a new function that prints the passed arguments as
// colorized with color.Print().
func (c *Color) PrintFunc() func(a ...interface{}) {
return func(a ...interface{}) {
c.Print(a...)
}
}
// FprintfFunc returns a new function that prints the passed arguments as
// colorized with color.Fprintf().
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
return func(w io.Writer, format string, a ...interface{}) {
c.Fprintf(w, format, a...)
}
}
// PrintfFunc returns a new function that prints the passed arguments as
// colorized with color.Printf().
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
return func(format string, a ...interface{}) {
c.Printf(format, a...)
}
}
// FprintlnFunc returns a new function that prints the passed arguments as
// colorized with color.Fprintln().
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
return func(w io.Writer, a ...interface{}) {
c.Fprintln(w, a...)
}
}
// PrintlnFunc returns a new function that prints the passed arguments as
// colorized with color.Println().
func (c *Color) PrintlnFunc() func(a ...interface{}) {
return func(a ...interface{}) {
c.Println(a...)
}
}
// SprintFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprint(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output, example:
//
// put := New(FgYellow).SprintFunc()
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
func (c *Color) SprintFunc() func(a ...interface{}) string {
return func(a ...interface{}) string {
return c.wrap(fmt.Sprint(a...))
}
}
// SprintfFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
return func(format string, a ...interface{}) string {
return c.wrap(fmt.Sprintf(format, a...))
}
}
// SprintlnFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
// string. Windows users should use this in conjunction with color.Output.
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
return func(a ...interface{}) string {
return c.wrap(fmt.Sprintln(a...))
}
}
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
// an example output might be: "1;36" -> bold cyan
func (c *Color) sequence() string {
format := make([]string, len(c.params))
for i, v := range c.params {
format[i] = strconv.Itoa(int(v))
}
return strings.Join(format, ";")
}
// wrap wraps the s string with the colors attributes. The string is ready to
// be printed.
func (c *Color) wrap(s string) string {
if c.isNoColorSet() {
return s
}
return c.format() + s + c.unformat()
}
func (c *Color) format() string {
return fmt.Sprintf("%s[%sm", escape, c.sequence())
}
func (c *Color) unformat() string {
return fmt.Sprintf("%s[%dm", escape, Reset)
}
// DisableColor disables the color output. Useful to not change any existing
// code and still being able to output. Can be used for flags like
// "--no-color". To enable back use EnableColor() method.
func (c *Color) DisableColor() {
c.noColor = boolPtr(true)
}
// EnableColor enables the color output. Use it in conjunction with
// DisableColor(). Otherwise this method has no side effects.
func (c *Color) EnableColor() {
c.noColor = boolPtr(false)
}
func (c *Color) isNoColorSet() bool {
// check first if we have user setted action
if c.noColor != nil {
return *c.noColor
}
// if not return the global option, which is disabled by default
return NoColor
}
// Equals returns a boolean value indicating whether two colors are equal.
func (c *Color) Equals(c2 *Color) bool {
if len(c.params) != len(c2.params) {
return false
}
for _, attr := range c.params {
if !c2.attrExists(attr) {
return false
}
}
return true
}
func (c *Color) attrExists(a Attribute) bool {
for _, attr := range c.params {
if attr == a {
return true
}
}
return false
}
func boolPtr(v bool) *bool {
return &v
}
func getCachedColor(p Attribute) *Color {
colorsCacheMu.Lock()
defer colorsCacheMu.Unlock()
c, ok := colorsCache[p]
if !ok {
c = New(p)
colorsCache[p] = c
}
return c
}
func colorPrint(format string, p Attribute, a ...interface{}) {
c := getCachedColor(p)
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
if len(a) == 0 {
c.Print(format)
} else {
c.Printf(format, a...)
}
}
func colorString(format string, p Attribute, a ...interface{}) string {
c := getCachedColor(p)
if len(a) == 0 {
return c.SprintFunc()(format)
}
return c.SprintfFunc()(format, a...)
}
// Black is a convenient helper function to print with black foreground. A
// newline is appended to format by default.
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
// Red is a convenient helper function to print with red foreground. A
// newline is appended to format by default.
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
// Green is a convenient helper function to print with green foreground. A
// newline is appended to format by default.
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
// Yellow is a convenient helper function to print with yellow foreground.
// A newline is appended to format by default.
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
// Blue is a convenient helper function to print with blue foreground. A
// newline is appended to format by default.
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
// Magenta is a convenient helper function to print with magenta foreground.
// A newline is appended to format by default.
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
// Cyan is a convenient helper function to print with cyan foreground. A
// newline is appended to format by default.
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
// White is a convenient helper function to print with white foreground. A
// newline is appended to format by default.
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
// BlackString is a convenient helper function to return a string with black
// foreground.
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
// RedString is a convenient helper function to return a string with red
// foreground.
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
// GreenString is a convenient helper function to return a string with green
// foreground.
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
// YellowString is a convenient helper function to return a string with yellow
// foreground.
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
// BlueString is a convenient helper function to return a string with blue
// foreground.
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
// MagentaString is a convenient helper function to return a string with magenta
// foreground.
func MagentaString(format string, a ...interface{}) string {
return colorString(format, FgMagenta, a...)
}
// CyanString is a convenient helper function to return a string with cyan
// foreground.
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
// WhiteString is a convenient helper function to return a string with white
// foreground.
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
// newline is appended to format by default.
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
// newline is appended to format by default.
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
// newline is appended to format by default.
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
// A newline is appended to format by default.
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
// newline is appended to format by default.
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
// A newline is appended to format by default.
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
// newline is appended to format by default.
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
// newline is appended to format by default.
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
// HiBlackString is a convenient helper function to return a string with hi-intensity black
// foreground.
func HiBlackString(format string, a ...interface{}) string {
return colorString(format, FgHiBlack, a...)
}
// HiRedString is a convenient helper function to return a string with hi-intensity red
// foreground.
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
// HiGreenString is a convenient helper function to return a string with hi-intensity green
// foreground.
func HiGreenString(format string, a ...interface{}) string {
return colorString(format, FgHiGreen, a...)
}
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
// foreground.
func HiYellowString(format string, a ...interface{}) string {
return colorString(format, FgHiYellow, a...)
}
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
// foreground.
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
// foreground.
func HiMagentaString(format string, a ...interface{}) string {
return colorString(format, FgHiMagenta, a...)
}
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
// foreground.
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
// foreground.
func HiWhiteString(format string, a ...interface{}) string {
return colorString(format, FgHiWhite, a...)
}
-342
View File
@@ -1,342 +0,0 @@
package color
import (
"bytes"
"fmt"
"os"
"testing"
"github.com/mattn/go-colorable"
)
// Testing colors is kinda different. First we test for given colors and their
// escaped formatted results. Next we create some visual tests to be tested.
// Each visual test includes the color name to be compared.
func TestColor(t *testing.T) {
rb := new(bytes.Buffer)
Output = rb
NoColor = false
testColors := []struct {
text string
code Attribute
}{
{text: "black", code: FgBlack},
{text: "red", code: FgRed},
{text: "green", code: FgGreen},
{text: "yellow", code: FgYellow},
{text: "blue", code: FgBlue},
{text: "magent", code: FgMagenta},
{text: "cyan", code: FgCyan},
{text: "white", code: FgWhite},
{text: "hblack", code: FgHiBlack},
{text: "hred", code: FgHiRed},
{text: "hgreen", code: FgHiGreen},
{text: "hyellow", code: FgHiYellow},
{text: "hblue", code: FgHiBlue},
{text: "hmagent", code: FgHiMagenta},
{text: "hcyan", code: FgHiCyan},
{text: "hwhite", code: FgHiWhite},
}
for _, c := range testColors {
New(c.code).Print(c.text)
line, _ := rb.ReadString('\n')
scannedLine := fmt.Sprintf("%q", line)
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
escapedForm := fmt.Sprintf("%q", colored)
fmt.Printf("%s\t: %s\n", c.text, line)
if scannedLine != escapedForm {
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
}
}
for _, c := range testColors {
line := New(c.code).Sprintf("%s", c.text)
scannedLine := fmt.Sprintf("%q", line)
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
escapedForm := fmt.Sprintf("%q", colored)
fmt.Printf("%s\t: %s\n", c.text, line)
if scannedLine != escapedForm {
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
}
}
}
func TestColorEquals(t *testing.T) {
fgblack1 := New(FgBlack)
fgblack2 := New(FgBlack)
bgblack := New(BgBlack)
fgbgblack := New(FgBlack, BgBlack)
fgblackbgred := New(FgBlack, BgRed)
fgred := New(FgRed)
bgred := New(BgRed)
if !fgblack1.Equals(fgblack2) {
t.Error("Two black colors are not equal")
}
if fgblack1.Equals(bgblack) {
t.Error("Fg and bg black colors are equal")
}
if fgblack1.Equals(fgbgblack) {
t.Error("Fg black equals fg/bg black color")
}
if fgblack1.Equals(fgred) {
t.Error("Fg black equals Fg red")
}
if fgblack1.Equals(bgred) {
t.Error("Fg black equals Bg red")
}
if fgblack1.Equals(fgblackbgred) {
t.Error("Fg black equals fg black bg red")
}
}
func TestNoColor(t *testing.T) {
rb := new(bytes.Buffer)
Output = rb
testColors := []struct {
text string
code Attribute
}{
{text: "black", code: FgBlack},
{text: "red", code: FgRed},
{text: "green", code: FgGreen},
{text: "yellow", code: FgYellow},
{text: "blue", code: FgBlue},
{text: "magent", code: FgMagenta},
{text: "cyan", code: FgCyan},
{text: "white", code: FgWhite},
{text: "hblack", code: FgHiBlack},
{text: "hred", code: FgHiRed},
{text: "hgreen", code: FgHiGreen},
{text: "hyellow", code: FgHiYellow},
{text: "hblue", code: FgHiBlue},
{text: "hmagent", code: FgHiMagenta},
{text: "hcyan", code: FgHiCyan},
{text: "hwhite", code: FgHiWhite},
}
for _, c := range testColors {
p := New(c.code)
p.DisableColor()
p.Print(c.text)
line, _ := rb.ReadString('\n')
if line != c.text {
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
}
}
// global check
NoColor = true
defer func() {
NoColor = false
}()
for _, c := range testColors {
p := New(c.code)
p.Print(c.text)
line, _ := rb.ReadString('\n')
if line != c.text {
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
}
}
}
func TestColorVisual(t *testing.T) {
// First Visual Test
Output = colorable.NewColorableStdout()
New(FgRed).Printf("red\t")
New(BgRed).Print(" ")
New(FgRed, Bold).Println(" red")
New(FgGreen).Printf("green\t")
New(BgGreen).Print(" ")
New(FgGreen, Bold).Println(" green")
New(FgYellow).Printf("yellow\t")
New(BgYellow).Print(" ")
New(FgYellow, Bold).Println(" yellow")
New(FgBlue).Printf("blue\t")
New(BgBlue).Print(" ")
New(FgBlue, Bold).Println(" blue")
New(FgMagenta).Printf("magenta\t")
New(BgMagenta).Print(" ")
New(FgMagenta, Bold).Println(" magenta")
New(FgCyan).Printf("cyan\t")
New(BgCyan).Print(" ")
New(FgCyan, Bold).Println(" cyan")
New(FgWhite).Printf("white\t")
New(BgWhite).Print(" ")
New(FgWhite, Bold).Println(" white")
fmt.Println("")
// Second Visual test
Black("black")
Red("red")
Green("green")
Yellow("yellow")
Blue("blue")
Magenta("magenta")
Cyan("cyan")
White("white")
HiBlack("hblack")
HiRed("hred")
HiGreen("hgreen")
HiYellow("hyellow")
HiBlue("hblue")
HiMagenta("hmagenta")
HiCyan("hcyan")
HiWhite("hwhite")
// Third visual test
fmt.Println()
Set(FgBlue)
fmt.Println("is this blue?")
Unset()
Set(FgMagenta)
fmt.Println("and this magenta?")
Unset()
// Fourth Visual test
fmt.Println()
blue := New(FgBlue).PrintlnFunc()
blue("blue text with custom print func")
red := New(FgRed).PrintfFunc()
red("red text with a printf func: %d\n", 123)
put := New(FgYellow).SprintFunc()
warn := New(FgRed).SprintFunc()
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
notice := New(FgBlue).FprintFunc()
notice(os.Stderr, "just a blue notice to stderr")
// Fifth Visual Test
fmt.Println()
fmt.Fprintln(Output, BlackString("black"))
fmt.Fprintln(Output, RedString("red"))
fmt.Fprintln(Output, GreenString("green"))
fmt.Fprintln(Output, YellowString("yellow"))
fmt.Fprintln(Output, BlueString("blue"))
fmt.Fprintln(Output, MagentaString("magenta"))
fmt.Fprintln(Output, CyanString("cyan"))
fmt.Fprintln(Output, WhiteString("white"))
fmt.Fprintln(Output, HiBlackString("hblack"))
fmt.Fprintln(Output, HiRedString("hred"))
fmt.Fprintln(Output, HiGreenString("hgreen"))
fmt.Fprintln(Output, HiYellowString("hyellow"))
fmt.Fprintln(Output, HiBlueString("hblue"))
fmt.Fprintln(Output, HiMagentaString("hmagenta"))
fmt.Fprintln(Output, HiCyanString("hcyan"))
fmt.Fprintln(Output, HiWhiteString("hwhite"))
}
func TestNoFormat(t *testing.T) {
fmt.Printf("%s %%s = ", BlackString("Black"))
Black("%s")
fmt.Printf("%s %%s = ", RedString("Red"))
Red("%s")
fmt.Printf("%s %%s = ", GreenString("Green"))
Green("%s")
fmt.Printf("%s %%s = ", YellowString("Yellow"))
Yellow("%s")
fmt.Printf("%s %%s = ", BlueString("Blue"))
Blue("%s")
fmt.Printf("%s %%s = ", MagentaString("Magenta"))
Magenta("%s")
fmt.Printf("%s %%s = ", CyanString("Cyan"))
Cyan("%s")
fmt.Printf("%s %%s = ", WhiteString("White"))
White("%s")
fmt.Printf("%s %%s = ", HiBlackString("HiBlack"))
HiBlack("%s")
fmt.Printf("%s %%s = ", HiRedString("HiRed"))
HiRed("%s")
fmt.Printf("%s %%s = ", HiGreenString("HiGreen"))
HiGreen("%s")
fmt.Printf("%s %%s = ", HiYellowString("HiYellow"))
HiYellow("%s")
fmt.Printf("%s %%s = ", HiBlueString("HiBlue"))
HiBlue("%s")
fmt.Printf("%s %%s = ", HiMagentaString("HiMagenta"))
HiMagenta("%s")
fmt.Printf("%s %%s = ", HiCyanString("HiCyan"))
HiCyan("%s")
fmt.Printf("%s %%s = ", HiWhiteString("HiWhite"))
HiWhite("%s")
}
func TestNoFormatString(t *testing.T) {
tests := []struct {
f func(string, ...interface{}) string
format string
args []interface{}
want string
}{
{BlackString, "%s", nil, "\x1b[30m%s\x1b[0m"},
{RedString, "%s", nil, "\x1b[31m%s\x1b[0m"},
{GreenString, "%s", nil, "\x1b[32m%s\x1b[0m"},
{YellowString, "%s", nil, "\x1b[33m%s\x1b[0m"},
{BlueString, "%s", nil, "\x1b[34m%s\x1b[0m"},
{MagentaString, "%s", nil, "\x1b[35m%s\x1b[0m"},
{CyanString, "%s", nil, "\x1b[36m%s\x1b[0m"},
{WhiteString, "%s", nil, "\x1b[37m%s\x1b[0m"},
{HiBlackString, "%s", nil, "\x1b[90m%s\x1b[0m"},
{HiRedString, "%s", nil, "\x1b[91m%s\x1b[0m"},
{HiGreenString, "%s", nil, "\x1b[92m%s\x1b[0m"},
{HiYellowString, "%s", nil, "\x1b[93m%s\x1b[0m"},
{HiBlueString, "%s", nil, "\x1b[94m%s\x1b[0m"},
{HiMagentaString, "%s", nil, "\x1b[95m%s\x1b[0m"},
{HiCyanString, "%s", nil, "\x1b[96m%s\x1b[0m"},
{HiWhiteString, "%s", nil, "\x1b[97m%s\x1b[0m"},
}
for i, test := range tests {
s := fmt.Sprintf("%s", test.f(test.format, test.args...))
if s != test.want {
t.Errorf("[%d] want: %q, got: %q", i, test.want, s)
}
}
}
-133
View File
@@ -1,133 +0,0 @@
/*
Package color is an ANSI color package to output colorized or SGR defined
output to the standard output. The API can be used in several way, pick one
that suits you.
Use simple and default helper functions with predefined foreground colors:
color.Cyan("Prints text in cyan.")
// a newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// More default foreground colors..
color.Red("We have red")
color.Yellow("Yellow color too!")
color.Magenta("And many others ..")
// Hi-intensity colors
color.HiGreen("Bright green color.")
color.HiBlack("Bright black means gray..")
color.HiWhite("Shiny white color!")
However there are times where custom color mixes are required. Below are some
examples to create custom color objects and use the print functions of each
separate color object.
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with White background.")
// Use your own io.Writer output
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
blue := color.New(color.FgBlue)
blue.Fprint(myWriter, "This will print text in blue.")
You can create PrintXxx functions to simplify even more:
// Create a custom print function for convenient
red := color.New(color.FgRed).PrintfFunc()
red("warning")
red("error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("don't forget this...")
You can also FprintXxx functions to pass your own io.Writer:
blue := color.New(FgBlue).FprintfFunc()
blue(myWriter, "important notice: %s", stars)
// Mix up with multiple attributes
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
success(myWriter, don't forget this...")
Or create SprintXxx functions to mix strings with other non-colorized strings:
yellow := New(FgYellow).SprintFunc()
red := New(FgRed).SprintFunc()
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Printf("this %s rocks!\n", info("package"))
Windows support is enabled by default. All Print functions work as intended.
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
set the output to color.Output:
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
Using with existing code is possible. Just use the Set() method to set the
standard output to the given parameters. That way a rewrite of an existing
code is not required.
// Use handy standard colors.
color.Set(color.FgYellow)
fmt.Println("Existing text will be now in Yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // use it in your function
fmt.Println("All text will be now bold magenta.")
There might be a case where you want to disable color output (for example to
pipe the standard output of your app to somewhere else). `Color` has support to
disable colors both globally and for single color definition. For example
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
the color output with:
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")
*/
package color

Some files were not shown because too many files have changed in this diff Show More