mirror of
https://github.com/irssi/irssi.git
synced 2026-08-16 23:22:16 +02:00
Merge 06d1c2b140 into 43f1727ef9
This commit is contained in:
commit
6b4cd41eb2
36 changed files with 870 additions and 2 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -89,3 +89,14 @@ subprojects/*
|
||||||
Irssi-Dist
|
Irssi-Dist
|
||||||
setup.cfg
|
setup.cfg
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
result
|
||||||
|
result-*
|
||||||
|
Build-fuzz
|
||||||
|
|
||||||
|
# Fuzzing artifacts (runtime corpus and crash/leak files)
|
||||||
|
corpus/
|
||||||
|
crash-*
|
||||||
|
leak-*
|
||||||
|
oom-*
|
||||||
|
timeout-*
|
||||||
|
slow-unit-*
|
||||||
|
|
|
||||||
179
README-NIX.md
Normal file
179
README-NIX.md
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# Building Irssi with Nix
|
||||||
|
|
||||||
|
This project includes a Nix flake for reproducible builds and development environments.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Nix](https://nixos.org/download.html) with flakes enabled
|
||||||
|
|
||||||
|
To enable flakes, add to `~/.config/nix/nix.conf`:
|
||||||
|
```
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build irssi
|
||||||
|
nix build
|
||||||
|
|
||||||
|
# Run irssi
|
||||||
|
./result/bin/irssi
|
||||||
|
|
||||||
|
# Enter development shell
|
||||||
|
nix develop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Packages
|
||||||
|
|
||||||
|
| Package | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `irssi` (default) | Full build with Perl scripting and proxy support |
|
||||||
|
| `irssi-minimal` | Build without Perl scripting support |
|
||||||
|
| `fuzz` | Fuzz targets with AddressSanitizer + UndefinedBehaviorSanitizer |
|
||||||
|
| `fuzz-asan` | Fuzz targets with AddressSanitizer only |
|
||||||
|
| `fuzz-ubsan` | Fuzz targets with UndefinedBehaviorSanitizer only |
|
||||||
|
|
||||||
|
### Building Packages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build default (full irssi)
|
||||||
|
nix build
|
||||||
|
|
||||||
|
# Build minimal variant
|
||||||
|
nix build .#irssi-minimal
|
||||||
|
|
||||||
|
# Build fuzzers (recommended: ASan + UBSan)
|
||||||
|
nix build .#fuzz
|
||||||
|
|
||||||
|
# Build fuzzers with only ASan (faster)
|
||||||
|
nix build .#fuzz-asan
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Shells
|
||||||
|
|
||||||
|
| Shell | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `default` | Standard development with gcc, gdb, valgrind |
|
||||||
|
| `fuzz` | Fuzzing development with clang + libFuzzer |
|
||||||
|
|
||||||
|
### Using Development Shells
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Standard development
|
||||||
|
nix develop
|
||||||
|
meson setup Build
|
||||||
|
ninja -C Build
|
||||||
|
ninja -C Build test
|
||||||
|
|
||||||
|
# Fuzzing development
|
||||||
|
nix develop .#fuzz
|
||||||
|
meson setup Build-fuzz -Dwith-perl=no -Dwithout-textui=yes -Dwith-fuzzer=yes
|
||||||
|
ninja -C Build-fuzz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fuzzing
|
||||||
|
|
||||||
|
The project includes five fuzz targets built with [libFuzzer](https://llvm.org/docs/LibFuzzer.html):
|
||||||
|
|
||||||
|
| Fuzzer | Tests |
|
||||||
|
|--------|-------|
|
||||||
|
| `irssi-fuzz` | Text formatting (`printtext_string()`) |
|
||||||
|
| `server-fuzz` | IRC protocol message parsing |
|
||||||
|
| `dcc-fuzz` | DCC protocol message parsing (SEND, CHAT, RESUME, ACCEPT) |
|
||||||
|
| `event-get-params-fuzz` | IRC event parameter parsing |
|
||||||
|
| `theme-load-fuzz` | Theme file loading |
|
||||||
|
|
||||||
|
### Building Fuzzers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build with ASan + UBSan (recommended for finding bugs)
|
||||||
|
nix build .#fuzz
|
||||||
|
|
||||||
|
# Build with ASan only (faster execution)
|
||||||
|
nix build .#fuzz-asan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Fuzzers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic fuzzing (creates corpus automatically)
|
||||||
|
./result/bin/irssi-fuzz corpus/irssi-fuzz/
|
||||||
|
|
||||||
|
# With dictionary (recommended for server-fuzz and dcc-fuzz)
|
||||||
|
./result/bin/server-fuzz -dict=src/fe-fuzz/tokens.txt corpus/server-fuzz/
|
||||||
|
./result/bin/dcc-fuzz -dict=src/fe-fuzz/dcc-tokens.txt corpus/dcc-fuzz/
|
||||||
|
|
||||||
|
# Limit number of runs
|
||||||
|
./result/bin/irssi-fuzz -runs=10000 corpus/irssi-fuzz/
|
||||||
|
|
||||||
|
# Parallel fuzzing (use multiple cores)
|
||||||
|
./result/bin/server-fuzz -fork=4 -dict=src/fe-fuzz/tokens.txt corpus/server-fuzz/
|
||||||
|
|
||||||
|
# Ignore memory leaks to focus on crashes
|
||||||
|
./result/bin/server-fuzz -detect_leaks=0 corpus/server-fuzz/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seed Corpus
|
||||||
|
|
||||||
|
Initial seed inputs are provided in `fuzz-corpora/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy seeds to corpus directories
|
||||||
|
mkdir -p corpus/irssi-fuzz corpus/server-fuzz corpus/dcc-fuzz corpus/event-get-params-fuzz corpus/theme-load-fuzz
|
||||||
|
cp fuzz-corpora/irssi-fuzz/* corpus/irssi-fuzz/
|
||||||
|
cp fuzz-corpora/server-fuzz/* corpus/server-fuzz/
|
||||||
|
cp fuzz-corpora/dcc-fuzz/* corpus/dcc-fuzz/
|
||||||
|
cp fuzz-corpora/event-get-params-fuzz/* corpus/event-get-params-fuzz/
|
||||||
|
cp fuzz-corpora/theme-load-fuzz/* corpus/theme-load-fuzz/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reproducing Crashes
|
||||||
|
|
||||||
|
When a fuzzer finds a crash, it saves the input to a file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Reproduce a crash
|
||||||
|
./result/bin/server-fuzz crash-<hash>
|
||||||
|
|
||||||
|
# Get more details with symbolized stack trace
|
||||||
|
ASAN_OPTIONS=symbolize=1 ./result/bin/server-fuzz crash-<hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fuzzer Input Formats
|
||||||
|
|
||||||
|
- **irssi-fuzz**: Arbitrary text, may contain irssi format codes (`%B`, `%U`, etc.)
|
||||||
|
- **server-fuzz**: Byte 0 selects prefix mode, remaining bytes are `\r\n`-separated IRC messages
|
||||||
|
- **dcc-fuzz**: Byte 0 selects DCC type (0=SEND, 1=CHAT, 2=RESUME, 3=ACCEPT, 4=GET cmd, 5=CLOSE cmd, 6=raw), remaining bytes are DCC message content
|
||||||
|
- **event-get-params-fuzz**: Byte 0 selects parsing mode (0-7), remaining bytes are parameters
|
||||||
|
- **theme-load-fuzz**: irssi theme file format
|
||||||
|
|
||||||
|
## Continuous Integration
|
||||||
|
|
||||||
|
To check that everything builds:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### UBSan warnings about function pointer types
|
||||||
|
|
||||||
|
The warning about `signals.c` function pointer types is expected:
|
||||||
|
```
|
||||||
|
runtime error: call to function through pointer to incorrect function type
|
||||||
|
```
|
||||||
|
|
||||||
|
This is due to irssi's dynamic signal dispatch system and is a known pattern in the codebase.
|
||||||
|
|
||||||
|
### Fuzzer stops immediately
|
||||||
|
|
||||||
|
Ensure the corpus directory exists:
|
||||||
|
```bash
|
||||||
|
mkdir -p corpus/irssi-fuzz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build fails with "perl not found"
|
||||||
|
|
||||||
|
Perl is required even for minimal/fuzzer builds (for generating help files). The Nix flake handles this automatically.
|
||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1766651565,
|
||||||
|
"narHash": "sha256-QEhk0eXgyIqTpJ/ehZKg9IKS7EtlWxF3N7DXy42zPfU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "3e2499d5539c16d0d173ba53552a4ff8547f4539",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
240
flake.nix
Normal file
240
flake.nix
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
{
|
||||||
|
description = "Irssi - A modular text mode chat client with IRC support";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
# Common build inputs for both the package and dev shell
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
glib
|
||||||
|
openssl
|
||||||
|
ncurses
|
||||||
|
perl
|
||||||
|
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
|
||||||
|
utf8proc
|
||||||
|
];
|
||||||
|
|
||||||
|
# Native build inputs (tools needed at build time)
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
perl
|
||||||
|
];
|
||||||
|
|
||||||
|
# Build fuzzers with clang and libfuzzer
|
||||||
|
# sanitizers: list of sanitizers to enable (e.g., ["address" "undefined"])
|
||||||
|
mkFuzzerPackage = { sanitizers ? [ "address" "undefined" ] }:
|
||||||
|
let
|
||||||
|
# Use clang's stdenv for libfuzzer support
|
||||||
|
clangStdenv = pkgs.llvmPackages.stdenv;
|
||||||
|
|
||||||
|
# Build sanitizer flags for linking (includes fuzzer)
|
||||||
|
sanitizerFlags = pkgs.lib.concatMapStringsSep "," (s: s) sanitizers;
|
||||||
|
fullSanitizerFlags =
|
||||||
|
if sanitizers == [] then "-fsanitize=fuzzer"
|
||||||
|
else "-fsanitize=fuzzer,${sanitizerFlags}";
|
||||||
|
|
||||||
|
# Compile-time sanitizer flags (without fuzzer - meson adds fuzzer-no-link)
|
||||||
|
compileSanitizerFlags =
|
||||||
|
if sanitizers == [] then ""
|
||||||
|
else "-fsanitize=${sanitizerFlags}";
|
||||||
|
in
|
||||||
|
clangStdenv.mkDerivation {
|
||||||
|
pname = "irssi-fuzz";
|
||||||
|
version = "1.5-head";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
perl # Needed at build time for generating help files
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
glib
|
||||||
|
openssl
|
||||||
|
ncurses
|
||||||
|
];
|
||||||
|
|
||||||
|
# Use preConfigure to set CFLAGS/LDFLAGS since meson's -Dc_args
|
||||||
|
# breaks the initial compiler test when sanitizers are involved
|
||||||
|
preConfigure = pkgs.lib.optionalString (sanitizers != []) ''
|
||||||
|
export CFLAGS="-g -O1 -fno-omit-frame-pointer ${compileSanitizerFlags}"
|
||||||
|
export LDFLAGS="${compileSanitizerFlags}"
|
||||||
|
'';
|
||||||
|
|
||||||
|
mesonFlags = [
|
||||||
|
"-Dwith-perl=no"
|
||||||
|
"-Dwithout-textui=yes"
|
||||||
|
"-Dwith-fuzzer=yes"
|
||||||
|
"-Dwith-fuzzer-lib=${fullSanitizerFlags}"
|
||||||
|
"-Dfuzzer-link-language=c"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Only install the fuzzer binaries
|
||||||
|
postInstall = ''
|
||||||
|
# Remove non-fuzzer files if any were installed
|
||||||
|
rm -rf $out/share $out/include $out/lib/pkgconfig || true
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "Irssi fuzz testing targets built with libFuzzer";
|
||||||
|
homepage = "https://irssi.org/";
|
||||||
|
license = licenses.gpl2Plus;
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = {
|
||||||
|
default = self.packages.${system}.irssi;
|
||||||
|
|
||||||
|
irssi = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "irssi";
|
||||||
|
version = "1.5-head";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
inherit nativeBuildInputs buildInputs;
|
||||||
|
|
||||||
|
mesonFlags = [
|
||||||
|
"-Dwith-perl=yes"
|
||||||
|
"-Dwith-proxy=yes"
|
||||||
|
];
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "A modular text mode chat client with IRC support";
|
||||||
|
homepage = "https://irssi.org/";
|
||||||
|
license = licenses.gpl2Plus;
|
||||||
|
maintainers = [];
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Variant without Perl scripting support
|
||||||
|
irssi-minimal = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "irssi-minimal";
|
||||||
|
version = "1.5-head";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
perl # Perl is needed at build time for generating help files
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
glib
|
||||||
|
openssl
|
||||||
|
ncurses
|
||||||
|
];
|
||||||
|
|
||||||
|
mesonFlags = [
|
||||||
|
"-Dwith-perl=no"
|
||||||
|
];
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "A modular text mode chat client with IRC support (minimal build)";
|
||||||
|
homepage = "https://irssi.org/";
|
||||||
|
license = licenses.gpl2Plus;
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fuzzers with AddressSanitizer + UndefinedBehaviorSanitizer (recommended)
|
||||||
|
fuzz = mkFuzzerPackage {
|
||||||
|
sanitizers = [ "address" "undefined" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fuzzers with only AddressSanitizer (faster, catches memory errors)
|
||||||
|
fuzz-asan = mkFuzzerPackage {
|
||||||
|
sanitizers = [ "address" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fuzzers with only UndefinedBehaviorSanitizer
|
||||||
|
fuzz-ubsan = mkFuzzerPackage {
|
||||||
|
sanitizers = [ "undefined" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells = {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
name = "irssi-dev";
|
||||||
|
|
||||||
|
inherit buildInputs;
|
||||||
|
|
||||||
|
nativeBuildInputs = nativeBuildInputs ++ (with pkgs; [
|
||||||
|
# Additional development tools
|
||||||
|
gdb
|
||||||
|
valgrind
|
||||||
|
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
|
||||||
|
strace
|
||||||
|
]);
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "Irssi development environment"
|
||||||
|
echo ""
|
||||||
|
echo "Build commands:"
|
||||||
|
echo " meson setup Build"
|
||||||
|
echo " ninja -C Build"
|
||||||
|
echo ""
|
||||||
|
echo "Run tests:"
|
||||||
|
echo " ninja -C Build test"
|
||||||
|
echo ""
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Development shell for fuzzing
|
||||||
|
fuzz = pkgs.mkShell.override { stdenv = pkgs.llvmPackages.stdenv; } {
|
||||||
|
name = "irssi-fuzz-dev";
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
glib
|
||||||
|
openssl
|
||||||
|
ncurses
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
meson
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
perl
|
||||||
|
# Fuzzing tools
|
||||||
|
llvmPackages.llvm # For llvm-symbolizer, llvm-cov, etc.
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "Irssi fuzzing development environment (clang + libFuzzer)"
|
||||||
|
echo ""
|
||||||
|
echo "Build fuzzers:"
|
||||||
|
echo " meson setup Build-fuzz -Dwith-perl=no -Dwithout-textui=yes -Dwith-fuzzer=yes"
|
||||||
|
echo " ninja -C Build-fuzz"
|
||||||
|
echo ""
|
||||||
|
echo "Fuzz targets will be in Build-fuzz/src/fe-fuzz/:"
|
||||||
|
echo " - irssi-fuzz"
|
||||||
|
echo " - server-fuzz"
|
||||||
|
echo " - event-get-params-fuzz (in irc/core/)"
|
||||||
|
echo " - theme-load-fuzz (in fe-common/core/)"
|
||||||
|
echo ""
|
||||||
|
echo "Run a fuzzer:"
|
||||||
|
echo " ./Build-fuzz/src/fe-fuzz/irssi-fuzz corpus/"
|
||||||
|
echo ""
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
4
fuzz-corpora/.gitignore
vendored
Normal file
4
fuzz-corpora/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Ignore libFuzzer-generated corpus files (40-char SHA1 hashes)
|
||||||
|
# Only manually-created seed files with descriptive names should be committed here
|
||||||
|
# Runtime corpus should go in corpus/ (which is gitignored at the repo root)
|
||||||
|
[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_accept_basic.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_accept_basic.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
test.txt 1234 100
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_chat_basic.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_chat_basic.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
chat 3232235777 1234
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_close_cmd.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_close_cmd.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
SEND testnick testfile.txt
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_get_cmd.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_get_cmd.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
testnick testfile.txt
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_raw_ctcp.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_raw_ctcp.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
UNKNOWN test data
|
||||||
1
fuzz-corpora/dcc-fuzz/dcc_resume_basic.seed
Normal file
1
fuzz-corpora/dcc-fuzz/dcc_resume_basic.seed
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
test.txt 1234 100
|
||||||
BIN
fuzz-corpora/dcc-fuzz/dcc_send_basic.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_basic.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/dcc-fuzz/dcc_send_ipv6.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_ipv6.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/dcc-fuzz/dcc_send_minimal.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_minimal.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/dcc-fuzz/dcc_send_multiword.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_multiword.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/dcc-fuzz/dcc_send_passive.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_passive.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/dcc-fuzz/dcc_send_quoted.seed
Normal file
BIN
fuzz-corpora/dcc-fuzz/dcc_send_quoted.seed
Normal file
Binary file not shown.
BIN
fuzz-corpora/event-get-params-fuzz/seed1
Normal file
BIN
fuzz-corpora/event-get-params-fuzz/seed1
Normal file
Binary file not shown.
1
fuzz-corpora/event-get-params-fuzz/seed2
Normal file
1
fuzz-corpora/event-get-params-fuzz/seed2
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
user1 user2 user3
|
||||||
1
fuzz-corpora/event-get-params-fuzz/seed3
Normal file
1
fuzz-corpora/event-get-params-fuzz/seed3
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
:server PRIVMSG #channel :test
|
||||||
1
fuzz-corpora/event-get-params-fuzz/seed4
Normal file
1
fuzz-corpora/event-get-params-fuzz/seed4
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
one two three four
|
||||||
1
fuzz-corpora/irssi-fuzz/seed1
Normal file
1
fuzz-corpora/irssi-fuzz/seed1
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Hello world
|
||||||
1
fuzz-corpora/irssi-fuzz/seed2
Normal file
1
fuzz-corpora/irssi-fuzz/seed2
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Test with colors
|
||||||
1
fuzz-corpora/irssi-fuzz/seed3
Normal file
1
fuzz-corpora/irssi-fuzz/seed3
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
%Bbold%n %Uunderline%n %9reverse%n
|
||||||
BIN
fuzz-corpora/server-fuzz/seed1
Normal file
BIN
fuzz-corpora/server-fuzz/seed1
Normal file
Binary file not shown.
BIN
fuzz-corpora/server-fuzz/seed2
Normal file
BIN
fuzz-corpora/server-fuzz/seed2
Normal file
Binary file not shown.
BIN
fuzz-corpora/server-fuzz/seed3
Normal file
BIN
fuzz-corpora/server-fuzz/seed3
Normal file
Binary file not shown.
1
fuzz-corpora/server-fuzz/seed4
Normal file
1
fuzz-corpora/server-fuzz/seed4
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
:nick!user@host JOIN #channel
|
||||||
BIN
fuzz-corpora/server-fuzz/seed5
Normal file
BIN
fuzz-corpora/server-fuzz/seed5
Normal file
Binary file not shown.
BIN
fuzz-corpora/server-fuzz/seed6
Normal file
BIN
fuzz-corpora/server-fuzz/seed6
Normal file
Binary file not shown.
5
fuzz-corpora/theme-load-fuzz/seed1
Normal file
5
fuzz-corpora/theme-load-fuzz/seed1
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
abstracts = {
|
||||||
|
line_start = "%B-%W!%B-%n ";
|
||||||
|
timestamp = "%K$0-%n ";
|
||||||
|
hilight = "%_$*%_";
|
||||||
|
};
|
||||||
2
fuzz-corpora/theme-load-fuzz/seed2
Normal file
2
fuzz-corpora/theme-load-fuzz/seed2
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
default_color = "-1";
|
||||||
|
replaces = { "[]=" = "%K$*%n"; };
|
||||||
89
src/fe-fuzz/dcc-tokens.txt
Normal file
89
src/fe-fuzz/dcc-tokens.txt
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# DCC protocol fuzzer dictionary
|
||||||
|
# DCC command types
|
||||||
|
"SEND"
|
||||||
|
"GET"
|
||||||
|
"CHAT"
|
||||||
|
"RESUME"
|
||||||
|
"ACCEPT"
|
||||||
|
"REJECT"
|
||||||
|
"SERVER"
|
||||||
|
|
||||||
|
# DCC server protocol numbers
|
||||||
|
"100"
|
||||||
|
"101"
|
||||||
|
"110"
|
||||||
|
"120"
|
||||||
|
"121"
|
||||||
|
|
||||||
|
# CTCP markers
|
||||||
|
"\x01"
|
||||||
|
"\x01DCC"
|
||||||
|
"\x01DCC SEND"
|
||||||
|
"\x01DCC CHAT"
|
||||||
|
|
||||||
|
# Special IP values
|
||||||
|
"16843009"
|
||||||
|
"3232235777"
|
||||||
|
"127.0.0.1"
|
||||||
|
"::1"
|
||||||
|
|
||||||
|
# Port values
|
||||||
|
"0"
|
||||||
|
"1234"
|
||||||
|
"6667"
|
||||||
|
"65535"
|
||||||
|
|
||||||
|
# File sizes
|
||||||
|
"1"
|
||||||
|
"100"
|
||||||
|
"1000"
|
||||||
|
"4294967295"
|
||||||
|
|
||||||
|
# Passive IDs
|
||||||
|
"42"
|
||||||
|
"63"
|
||||||
|
|
||||||
|
# Delimiters and quoting
|
||||||
|
"\""
|
||||||
|
" "
|
||||||
|
":"
|
||||||
|
|
||||||
|
# Protocol prefixes
|
||||||
|
"DCC "
|
||||||
|
"CTCP_MESSAGE "
|
||||||
|
"CTCP_REPLY "
|
||||||
|
"ACTION "
|
||||||
|
|
||||||
|
# DCC server flags
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"s"
|
||||||
|
"c"
|
||||||
|
"f"
|
||||||
|
"S"
|
||||||
|
"C"
|
||||||
|
"F"
|
||||||
|
"+s"
|
||||||
|
"+c"
|
||||||
|
"+f"
|
||||||
|
"-s"
|
||||||
|
"-c"
|
||||||
|
"-f"
|
||||||
|
|
||||||
|
# Common filename patterns
|
||||||
|
"test.txt"
|
||||||
|
"file.txt"
|
||||||
|
"\"file with spaces.txt\""
|
||||||
|
"file_with_underscores.txt"
|
||||||
|
|
||||||
|
# Chat argument
|
||||||
|
"chat"
|
||||||
|
"CHAT"
|
||||||
|
|
||||||
|
# Query prefix for DCC chat
|
||||||
|
"="
|
||||||
|
|
||||||
|
# Common nicks
|
||||||
|
"nick"
|
||||||
|
"testnick"
|
||||||
|
"fuzzernick"
|
||||||
242
src/fe-fuzz/dcc.c
Normal file
242
src/fe-fuzz/dcc.c
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
dcc.c : irssi DCC fuzzer
|
||||||
|
|
||||||
|
Copyright (C) 2018 Joseph Bisch
|
||||||
|
Copyright (C) 2025 irssi contributors
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <irssi/src/irc/core/module.h>
|
||||||
|
#include <irssi/src/core/modules-load.h>
|
||||||
|
#include <irssi/src/fe-text/module-formats.h>
|
||||||
|
#include <irssi/src/core/levels.h>
|
||||||
|
#include <irssi/src/fe-common/core/themes.h>
|
||||||
|
#include <irssi/src/core/core.h>
|
||||||
|
#include <irssi/src/fe-common/core/fe-common-core.h>
|
||||||
|
#include <irssi/src/core/args.h>
|
||||||
|
#include <irssi/src/fe-common/core/printtext.h>
|
||||||
|
#include <irssi/src/core/misc.h>
|
||||||
|
#include <irssi/src/core/servers-setup.h>
|
||||||
|
#include <irssi/src/core/rawlog.h>
|
||||||
|
#include <irssi/src/core/network-openssl.h>
|
||||||
|
#include <irssi/src/core/net-sendbuffer.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <irssi/src/irc/core/irc.h>
|
||||||
|
#include <irssi/src/irc/core/irc-servers.h>
|
||||||
|
#include <irssi/src/irc/core/irc-channels.h>
|
||||||
|
#include <irssi/src/fe-fuzz/null-logger.h>
|
||||||
|
|
||||||
|
/* irc-core.c */
|
||||||
|
void irc_core_init(void);
|
||||||
|
void irc_core_deinit(void);
|
||||||
|
|
||||||
|
/* irc-session.c */
|
||||||
|
void irc_session_init(void);
|
||||||
|
void irc_session_deinit(void);
|
||||||
|
|
||||||
|
/* fe-common-irc.c */
|
||||||
|
void fe_common_irc_init(void);
|
||||||
|
void fe_common_irc_deinit(void);
|
||||||
|
|
||||||
|
SERVER_REC *server;
|
||||||
|
|
||||||
|
void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
|
||||||
|
{
|
||||||
|
char *params, *nick;
|
||||||
|
|
||||||
|
g_return_if_fail(server != NULL);
|
||||||
|
|
||||||
|
params = event_get_params(data, 1, &nick);
|
||||||
|
|
||||||
|
if (g_strcmp0(server->nick, nick) != 0) {
|
||||||
|
/* nick changed unexpectedly .. connected via proxy, etc. */
|
||||||
|
g_free(server->nick);
|
||||||
|
server->nick = g_strdup(nick);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the server address */
|
||||||
|
g_free(server->real_address);
|
||||||
|
server->real_address = from == NULL ?
|
||||||
|
g_strdup(server->connrec->address) : /* shouldn't happen.. */
|
||||||
|
g_strdup(from);
|
||||||
|
|
||||||
|
/* last welcome message found - commands can be sent to server now. */
|
||||||
|
server->connected = 1;
|
||||||
|
server->real_connect_time = time(NULL);
|
||||||
|
|
||||||
|
/* let the queue send now that we are identified */
|
||||||
|
g_get_current_time(&server->wait_cmd);
|
||||||
|
|
||||||
|
if (server->connrec->usermode != NULL) {
|
||||||
|
/* Send the user mode, before the autosendcmd.
|
||||||
|
* Do not pass this through cmd_mode because it
|
||||||
|
* is not known whether the resulting MODE message
|
||||||
|
* (if any) is the initial umode or a reply to this.
|
||||||
|
*/
|
||||||
|
irc_send_cmdv(server, "MODE %s %s", server->nick,
|
||||||
|
server->connrec->usermode);
|
||||||
|
g_free_not_null(server->wanted_usermode);
|
||||||
|
server->wanted_usermode = g_strdup(server->connrec->usermode);
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_emit("event connected", 1, server);
|
||||||
|
g_free(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irc_server_init_bare_minimum(IRC_SERVER_REC *server) {
|
||||||
|
server->rawlog = rawlog_create();
|
||||||
|
|
||||||
|
/* isupport is already created by server_init_connect, just populate it */
|
||||||
|
g_hash_table_insert(server->isupport, g_strdup("CHANMODES"), g_strdup("beI,k,l,imnpst"));
|
||||||
|
g_hash_table_insert(server->isupport, g_strdup("PREFIX"), g_strdup("(ohv)@%+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_server() {
|
||||||
|
CHAT_PROTOCOL_REC *proto;
|
||||||
|
SERVER_CONNECT_REC *conn;
|
||||||
|
GIOChannel *handle = g_io_channel_unix_new(open("/dev/null", O_RDWR));
|
||||||
|
g_io_channel_set_encoding(handle, NULL, NULL);
|
||||||
|
g_io_channel_set_close_on_unref(handle, TRUE);
|
||||||
|
|
||||||
|
proto = chat_protocol_find("IRC");
|
||||||
|
conn = server_create_conn(proto->id, "localhost", 0, "", "", "user");
|
||||||
|
server = proto->server_init_connect(conn);
|
||||||
|
server->session_reconnect = TRUE;
|
||||||
|
g_free(server->tag);
|
||||||
|
server->tag = g_strdup("testserver");
|
||||||
|
server->handle = net_sendbuffer_create(handle, 0);
|
||||||
|
|
||||||
|
/* we skip some initialisations that would try to send data */
|
||||||
|
irc_session_deinit();
|
||||||
|
irc_irc_deinit();
|
||||||
|
|
||||||
|
server_connect_finished(server);
|
||||||
|
|
||||||
|
/* make up for the skipped session init */
|
||||||
|
irc_server_init_bare_minimum(IRC_SERVER(server));
|
||||||
|
|
||||||
|
irc_irc_init();
|
||||||
|
irc_session_init();
|
||||||
|
|
||||||
|
server_connect_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||||
|
g_log_set_null_logger();
|
||||||
|
#endif
|
||||||
|
core_register_options();
|
||||||
|
fe_common_core_register_options();
|
||||||
|
/* no args */
|
||||||
|
args_execute(0, NULL);
|
||||||
|
core_preinit((*argv)[0]);
|
||||||
|
core_init();
|
||||||
|
irssi_ssl_init();
|
||||||
|
irc_core_init();
|
||||||
|
fe_common_core_init();
|
||||||
|
fe_common_irc_init();
|
||||||
|
signal_add("event 001", (SIGNAL_FUNC) event_connected);
|
||||||
|
module_register("core", "fe-fuzz");
|
||||||
|
rawlog_set_size(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DCC fuzzer input format:
|
||||||
|
* Byte 0: DCC type selector
|
||||||
|
* 0 = DCC SEND
|
||||||
|
* 1 = DCC CHAT
|
||||||
|
* 2 = DCC RESUME
|
||||||
|
* 3 = DCC ACCEPT
|
||||||
|
* 4 = DCC GET (command parsing)
|
||||||
|
* 5 = DCC CLOSE (command parsing)
|
||||||
|
* 6+ = raw CTCP DCC message
|
||||||
|
*
|
||||||
|
* Remaining bytes: DCC message content (after "DCC <type> ")
|
||||||
|
*/
|
||||||
|
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||||
|
gchar *copy;
|
||||||
|
gchar *ctcp_line;
|
||||||
|
gchar *irc_line;
|
||||||
|
int dcc_type;
|
||||||
|
int disconnected;
|
||||||
|
|
||||||
|
if (size < 2) return 0;
|
||||||
|
|
||||||
|
test_server();
|
||||||
|
|
||||||
|
dcc_type = data[0] % 7;
|
||||||
|
copy = g_strndup((const gchar *)data+1, size-1);
|
||||||
|
|
||||||
|
/* Replace any NUL bytes with spaces to allow fuzzing of full data */
|
||||||
|
for (size_t i = 0; i < size-1; i++) {
|
||||||
|
if (copy[i] == '\0') copy[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (dcc_type) {
|
||||||
|
case 0: /* DCC SEND - file transfer offer */
|
||||||
|
ctcp_line = g_strdup_printf("DCC SEND %s", copy);
|
||||||
|
break;
|
||||||
|
case 1: /* DCC CHAT - chat request */
|
||||||
|
ctcp_line = g_strdup_printf("DCC CHAT %s", copy);
|
||||||
|
break;
|
||||||
|
case 2: /* DCC RESUME - resume file transfer */
|
||||||
|
ctcp_line = g_strdup_printf("DCC RESUME %s", copy);
|
||||||
|
break;
|
||||||
|
case 3: /* DCC ACCEPT - accept resume */
|
||||||
|
ctcp_line = g_strdup_printf("DCC ACCEPT %s", copy);
|
||||||
|
break;
|
||||||
|
case 4: /* DCC GET command parsing */
|
||||||
|
/* Test the command parsing path via "dcc get" command */
|
||||||
|
signal_emit("command dcc get", 3, copy, server, NULL);
|
||||||
|
g_free(copy);
|
||||||
|
goto cleanup;
|
||||||
|
case 5: /* DCC CLOSE command parsing */
|
||||||
|
/* Test the command parsing path via "dcc close" command */
|
||||||
|
signal_emit("command dcc close", 3, copy, server, NULL);
|
||||||
|
g_free(copy);
|
||||||
|
goto cleanup;
|
||||||
|
default: /* Raw CTCP DCC message */
|
||||||
|
ctcp_line = g_strdup_printf("DCC %s", copy);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit the DCC CTCP message signal directly
|
||||||
|
* This is what happens when a CTCP message is received:
|
||||||
|
* server, data, nick, addr, target, chat */
|
||||||
|
server_ref(server);
|
||||||
|
signal_emit("ctcp msg dcc", 6, server, ctcp_line,
|
||||||
|
"fuzzernick", "fuzzer@host.example.com", "testnick", NULL);
|
||||||
|
disconnected = server->disconnected;
|
||||||
|
server_unref(server);
|
||||||
|
|
||||||
|
g_free(ctcp_line);
|
||||||
|
g_free(copy);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (server->disconnected) {
|
||||||
|
test_server();
|
||||||
|
} else {
|
||||||
|
server_disconnect(server);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -47,6 +47,29 @@ executable(
|
||||||
dependencies : dep,
|
dependencies : dep,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
executable('dcc-fuzz',
|
||||||
|
files(
|
||||||
|
'null-logger.c',
|
||||||
|
'dcc.c',
|
||||||
|
'../fe-text/module-formats.c',
|
||||||
|
),
|
||||||
|
link_with : [
|
||||||
|
libconfig_a,
|
||||||
|
libcore_a,
|
||||||
|
libfuzzer_fe_common_core_a,
|
||||||
|
libirc_core_a,
|
||||||
|
libfe_common_irc_a,
|
||||||
|
libfe_irc_dcc_a,
|
||||||
|
libfe_irc_notifylist_a,
|
||||||
|
],
|
||||||
|
link_args : [fuzzer_lib],
|
||||||
|
link_language : fuzzer_link_language,
|
||||||
|
include_directories : rootinc,
|
||||||
|
implicit_include_directories : false,
|
||||||
|
install : true,
|
||||||
|
dependencies : dep
|
||||||
|
)
|
||||||
|
|
||||||
# noinst_headers = files(
|
# noinst_headers = files(
|
||||||
# 'null-logger.h',
|
# 'null-logger.h',
|
||||||
# '../fe-text/module-formats.h',
|
# '../fe-text/module-formats.h',
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,8 @@ void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
|
||||||
|
|
||||||
void irc_server_init_bare_minimum(IRC_SERVER_REC *server) {
|
void irc_server_init_bare_minimum(IRC_SERVER_REC *server) {
|
||||||
server->rawlog = rawlog_create();
|
server->rawlog = rawlog_create();
|
||||||
server->isupport = g_hash_table_new((GHashFunc) i_istr_hash, (GCompareFunc) i_istr_equal);
|
|
||||||
|
|
||||||
/* set the standards */
|
/* isupport is already created by server_init_connect, just populate it */
|
||||||
g_hash_table_insert(server->isupport, g_strdup("CHANMODES"), g_strdup("beI,k,l,imnpst"));
|
g_hash_table_insert(server->isupport, g_strdup("CHANMODES"), g_strdup("beI,k,l,imnpst"));
|
||||||
g_hash_table_insert(server->isupport, g_strdup("PREFIX"), g_strdup("(ohv)@%+"));
|
g_hash_table_insert(server->isupport, g_strdup("PREFIX"), g_strdup("(ohv)@%+"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue