Skip to content

Commit

Permalink
Merge pull request #9 from PretendoNetwork/dev
Browse files Browse the repository at this point in the history
Merge dev to master
  • Loading branch information
jonbarrow authored Oct 13, 2023
2 parents 348f43d + cc9b839 commit 1654f0b
Show file tree
Hide file tree
Showing 115 changed files with 2,209 additions and 1,238 deletions.
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.env
.git
build
log
go.work
*.test
go.work.sum
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# --- builder ---
FROM golang:1.20.6-alpine3.17 as builder
LABEL stage=builder
RUN apk add git
WORKDIR /build

COPY go.* ./
RUN go mod download

COPY . ./
ARG BUILD_STRING=pretendo.friends.docker
RUN go build -ldflags "-X 'main.serverBuildString=${BUILD_STRING}'" -v -o server

# --- runner ---
FROM alpine:3.17 as runner
WORKDIR /build

COPY --from=builder /build/server /build/
CMD ["/build/server"]
64 changes: 64 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# TODO - Assumes a UNIX-like OS

RED := $(shell tput setaf 1)
BLUE := $(shell tput setaf 4)
CYAN := $(shell tput setaf 14)
ORANGE := $(shell tput setaf 202)
YELLOW := $(shell tput setaf 214)
RESET := $(shell tput sgr0)

ifeq ($(shell which go),)
# TODO - Read contents from .git folder instead?
$(error "$(RED)go command not found. Install go to continue $(BLUE)https://go.dev/doc/install$(RESET)")
endif

ifneq ($(wildcard .git),)
# * .git folder exists, build server build string from repo info
ifeq ($(shell which git),)
# TODO - Read contents from .git folder instead?
$(error "$(RED)git command not found. Install git to continue $(ORANGE)https://git-scm.com/downloads$(RESET)")
endif
$(info "$(CYAN)Building server build string from repository info$(RESET)")
# * Build server build string from repo info
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
REMOTE_ORIGIN := $(shell git config --get remote.origin.url)

# * Handle multiple origin URL formats
HTTPS_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 8)
HTTP_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 7)
GIT@_PREFIX_CHECK := $(shell echo $(REMOTE_ORIGIN) | head -c 4)

ifeq ($(HTTPS_PREFIX_CHECK), https://)
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f4-)
else ifeq ($(HTTP_PREFIX_CHECK), http://)
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f4-)
else ifeq ($(GIT@_PREFIX_CHECK), git@)
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d: -f2-)
else
REMOTE_PATH := $(shell echo $(REMOTE_ORIGIN) | cut -d/ -f2-)
endif

HASH := $(shell git rev-parse --short HEAD)
SERVER_BUILD := $(BRANCH):$(REMOTE_PATH)@$(HASH)

else
# * .git folder not present, assume downloaded from zip file and just use folder name
$(info "$(CYAN)git repository not found. Building server build string from folder name$(RESET)")
SERVER_BUILD := friends
endif

# * Final build string
DATE_TIME := $(shell date --iso=seconds)
BUILD_STRING := $(SERVER_BUILD), $(DATE_TIME)

default:
ifeq ($(wildcard .env),)
$(warning "$(YELLOW).env file not found, environment variables may not be populated correctly$(RESET)")
endif
go get -u
go mod tidy
go build -ldflags "-X 'main.serverBuildString=$(BUILD_STRING)'" -o ./build/friends

docker:
docker build -t friends --build-arg BUILD_STRING="$(BUILD_STRING)" .
docker image prune --filter label=stage=builder -f
102 changes: 98 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,99 @@
# Friends - Secure
### Pretendo Friends secure server
# Friends replacement server
Includes both the authentication and secure servers

## About
Handles basic functionality for Wii U and 3DS friends. Many things are not implemented.
## Compiling

### Setup
Install [Go](https://go.dev/doc/install) and [git](https://git-scm.com/downloads), then clone and enter the repository

```bash
$ git clone https://github.com/PretendoNetwork/friends
$ cd friends
```

### Compiling and running using `docker` (Preferred)
Install Docker either through your systems package manager or the [official installer](https://docs.docker.com/get-docker/)

To build the container:

```bash
$ docker build -t friends .
$ docker image prune --filter label=stage=builder -f
```
Optionally you may provide `BUILD_STRING` to `--build-arg` to set the authentication server build string

```bash
$ docker build -t friends --build-arg BUILD_STRING=auth-build-string .
$ docker image prune --filter label=stage=builder -f
```
If `BUILD_STRING` is not set, the default build string `pretendo.friends.docker` is used. You may also use the `docker` rule when building with `make` to set the build string automatically. See [compiling using `make`](#compiling-using-make) below for more info

To run the image first create a `.env` file with your [Configuration](#configuration) set before using `docker run`

Example:
```
PN_FRIENDS_POSTGRES_URI=postgres://username:password@localhost/friends?sslmode=disable
PN_FRIENDS_AUTHENTICATION_SERVER_PORT=60000
...
```

```bash
$ docker run --name friends --env-file .env -it friends
```

The image is compatible popular container managers such as Docker Compose and Portainer

### Compiling using `go`
To compile using Go, `go get` the required modules and then `go build` to your desired location. You may also want to tidy the go modules, though this is optional

```bash
$ go get -u
$ go mod tidy
$ go build -o build/friends
```

The server is now built to `build/friends`

When compiling with only Go, the authentication servers build string is not automatically set. This should not cause any issues with gameplay, but it means that the server build will not be visible in any packet dumps or logs a title may produce

To compile the servers with the authentication server build string, add `-ldflags "-X 'main.serverBuildString=BUILD_STRING_HERE'"` to the build command, or use `make` to compile the server

### Compiling using `make`
Compiling using `make` will read the local `.git` directory to create a dynamic authentication server build string, based on your repositories remote origin and current commit

Install `make` either through your systems package manager or the [official download](https://www.gnu.org/software/make/). We provide two different rules; A `default` rule which compiles [using `go`](#compiling-using-go), and a `docker` rule which compiles [using `docker`](#compiling-and-running-using-docker-preferred). Please refer to each sections setup instructions before continuing with your preferred rule

To build using `go`

```bash
$ make
```

The server is now built to `build/friends`

To build using `docker`

```bash
$ make docker
```

The image is now ready to run

## Configuration
All configuration options are handled via environment variables

`.env` files are supported

| Name | Description | Required |
|-----------------------------------------|------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
| `PN_FRIENDS_CONFIG_DATABASE_URI` | Fully qualified URI to your Postgres server (Example `postgres://username:password@localhost/friends?sslmode=disable`) | Yes |
| `PN_FRIENDS_CONFIG_KERBEROS_PASSWORD` | Password used as part of the internal server data in Kerberos tickets | No (Default password `password` will be used) |
| `PN_FRIENDS_CONFIG_AES_KEY` | AES key used in tokens provided by the account server | Yes |
| `PN_FRIENDS_CONFIG_GRPC_API_KEY` | API key for your GRPC server | No (Assumed to be an open gRPC API) |
| `PN_FRIENDS_GRPC_SERVER_PORT` | Port for the GRPC server | Yes |
| `PN_FRIENDS_AUTHENTICATION_SERVER_PORT` | Port for the authentication server | Yes |
| `PN_FRIENDS_SECURE_SERVER_HOST` | Host name for the secure server (should point to the same address as the authentication server) | Yes |
| `PN_FRIENDS_SECURE_SERVER_PORT` | Port for the secure server | Yes |
| `PN_FRIENDS_ACCOUNT_GRPC_HOST` | Host name for your account server gRPC service | Yes |
| `PN_FRIENDS_ACCOUNT_GRPC_PORT` | Port for your account server gRPC service | Yes |
| `PN_FRIENDS_ACCOUNT_GRPC_API_KEY` | API key for your account server gRPC service | No (Assumed to be an open gRPC API) |
27 changes: 11 additions & 16 deletions database/3ds/get_friend_miis.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
package database_3ds

import (
"database/sql"
"time"

"github.com/PretendoNetwork/friends-secure/database"
"github.com/PretendoNetwork/friends-secure/globals"
"github.com/PretendoNetwork/friends/database"
"github.com/PretendoNetwork/nex-go"
friends_3ds "github.com/PretendoNetwork/nex-protocols-go/friends/3ds"
friends_3ds_types "github.com/PretendoNetwork/nex-protocols-go/friends-3ds/types"
"github.com/lib/pq"
)

// Get a friend's mii
func GetFriendMiis(pids []uint32) []*friends_3ds.FriendMii {
friendMiis := make([]*friends_3ds.FriendMii, 0)
// GetFriendMiis returns the Mii of all friends
func GetFriendMiis(pids []uint32) ([]*friends_3ds_types.FriendMii, error) {
friendMiis := make([]*friends_3ds_types.FriendMii, 0)

rows, err := database.Postgres.Query(`
SELECT pid, mii_name, mii_data FROM "3ds".user_data WHERE pid IN ($1)`, database.PIDArrayToString(pids))
SELECT pid, mii_name, mii_data FROM "3ds".user_data WHERE pid=ANY($1::int[])`, pq.Array(pids))
if err != nil {
if err == sql.ErrNoRows {
globals.Logger.Warning(err.Error())
} else {
globals.Logger.Critical(err.Error())
}
return friendMiis, err
}

changedTime := nex.NewDateTime(0)
Expand All @@ -30,19 +25,19 @@ func GetFriendMiis(pids []uint32) []*friends_3ds.FriendMii {
for rows.Next() {
var pid uint32

mii := friends_3ds.NewMii()
mii := friends_3ds_types.NewMii()
mii.Unknown2 = false
mii.Unknown3 = 0

rows.Scan(&pid, &mii.Name, &mii.MiiData)

friendMii := friends_3ds.NewFriendMii()
friendMii := friends_3ds_types.NewFriendMii()
friendMii.PID = pid
friendMii.Mii = mii
friendMii.ModifiedAt = changedTime

friendMiis = append(friendMiis, friendMii)
}

return friendMiis
return friendMiis, nil
}
45 changes: 14 additions & 31 deletions database/3ds/get_friend_persistent_infos.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,43 @@
package database_3ds

import (
"database/sql"
"time"

"github.com/PretendoNetwork/friends-secure/database"
"github.com/PretendoNetwork/friends-secure/globals"
"github.com/PretendoNetwork/friends/database"
"github.com/PretendoNetwork/nex-go"
friends_3ds "github.com/PretendoNetwork/nex-protocols-go/friends/3ds"
friends_3ds_types "github.com/PretendoNetwork/nex-protocols-go/friends-3ds/types"
"github.com/lib/pq"
)

// Get a friend's persistent information
func GetFriendPersistentInfos(user1_pid uint32, pids []uint32) []*friends_3ds.FriendPersistentInfo {
persistentInfos := make([]*friends_3ds.FriendPersistentInfo, 0)
// GetFriendPersistentInfos returns the persistent information of all friends
func GetFriendPersistentInfos(user1_pid uint32, pids []uint32) ([]*friends_3ds_types.FriendPersistentInfo, error) {
persistentInfos := make([]*friends_3ds_types.FriendPersistentInfo, 0)

rows, err := database.Postgres.Query(`
SELECT pid, region, area, language, favorite_title, favorite_title_version, comment, comment_changed, last_online FROM "3ds".user_data WHERE pid IN ($1)`, database.PIDArrayToString(pids))
SELECT pid, region, area, language, favorite_title, favorite_title_version, comment, comment_changed, last_online, mii_changed FROM "3ds".user_data WHERE pid=ANY($1::int[])`, pq.Array(pids))
if err != nil {
if err == sql.ErrNoRows {
globals.Logger.Warning(err.Error())
} else {
globals.Logger.Critical(err.Error())
}
return persistentInfos, err
}

for rows.Next() {
persistentInfo := friends_3ds.NewFriendPersistentInfo()
persistentInfo := friends_3ds_types.NewFriendPersistentInfo()

gameKey := friends_3ds.NewGameKey()
gameKey := friends_3ds_types.NewGameKey()

var lastOnlineTime uint64
var msgUpdateTime uint64
var friendedAtTime uint64
var miiModifiedAtTime uint64

rows.Scan(
&persistentInfo.PID, &persistentInfo.Region, &persistentInfo.Area, &persistentInfo.Language,
&gameKey.TitleID, &gameKey.TitleVersion, &persistentInfo.Message, &msgUpdateTime, &lastOnlineTime)

err = database.Postgres.QueryRow(`
SELECT date FROM "3ds".friendships WHERE user1_pid=$1 AND user2_pid=$2 AND type=0 LIMIT 1`, user1_pid, persistentInfo.PID).Scan(&friendedAtTime)
if err != nil {
if err == sql.ErrNoRows {
friendedAtTime = uint64(time.Now().Unix())
} else {
globals.Logger.Critical(err.Error())
}
}
&gameKey.TitleID, &gameKey.TitleVersion, &persistentInfo.Message, &msgUpdateTime, &lastOnlineTime, &miiModifiedAtTime)

persistentInfo.MessageUpdatedAt = nex.NewDateTime(msgUpdateTime)
persistentInfo.FriendedAt = nex.NewDateTime(friendedAtTime)
persistentInfo.MiiModifiedAt = nex.NewDateTime(miiModifiedAtTime)
persistentInfo.LastOnline = nex.NewDateTime(lastOnlineTime)
persistentInfo.GameKey = gameKey
persistentInfo.Platform = 2 // Always 3DS

persistentInfos = append(persistentInfos, persistentInfo)
}

return persistentInfos
return persistentInfos, nil
}
23 changes: 13 additions & 10 deletions database/3ds/get_user_friends.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,31 @@ package database_3ds
import (
"database/sql"

"github.com/PretendoNetwork/friends-secure/database"
"github.com/PretendoNetwork/friends-secure/globals"
friends_3ds "github.com/PretendoNetwork/nex-protocols-go/friends/3ds"
"github.com/PretendoNetwork/friends/database"
friends_3ds_types "github.com/PretendoNetwork/nex-protocols-go/friends-3ds/types"
)

// Get all of a user's friend relationships
func GetUserFriends(pid uint32) []*friends_3ds.FriendRelationship {
friendRelationships := make([]*friends_3ds.FriendRelationship, 0)
// GetUserFriends returns all friend relationships of a user
func GetUserFriends(pid uint32) ([]*friends_3ds_types.FriendRelationship, error) {
friendRelationships := make([]*friends_3ds_types.FriendRelationship, 0)

rows, err := database.Postgres.Query(`
SELECT user2_pid, type FROM "3ds".friendships WHERE user1_pid=$1 AND type=1 LIMIT 100`, pid)
if err != nil && err != sql.ErrNoRows {
globals.Logger.Critical(err.Error())
if err != nil {
if err == sql.ErrNoRows {
return friendRelationships, database.ErrEmptyList
} else {
return friendRelationships, err
}
}

for rows.Next() {
relationship := friends_3ds.NewFriendRelationship()
relationship := friends_3ds_types.NewFriendRelationship()
relationship.LFC = 0
rows.Scan(&relationship.PID, &relationship.RelationshipType)

friendRelationships = append(friendRelationships, relationship)
}

return friendRelationships
return friendRelationships, nil
}
Loading

0 comments on commit 1654f0b

Please sign in to comment.