Skip to content

Commit

Permalink
Validate ip addresses (#176)
Browse files Browse the repository at this point in the history
* validate ip addresses

* fmt

* cleaning unused var
  • Loading branch information
sameh-farouk authored May 27, 2024
1 parent 82969c7 commit b09a8d3
Show file tree
Hide file tree
Showing 6 changed files with 7,070 additions and 200 deletions.
30 changes: 23 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@subsquid/typeorm-store": "0.2.2",
"axios": "^0.28.1",
"dotenv": "^16.0.0",
"ipaddr.js": "^2.2.0",
"lodash": "^4.17.21",
"pg": "^8.7.3",
"pg-format": "^1.0.4",
Expand Down
9 changes: 2 additions & 7 deletions src/mappings/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,8 @@ export async function contractCreated(
})

if (contract.publicIps > 0 && touchedIps.length == 0) {
console.log(`something went wrong with contract ${contractEvent.contractId}`)
console.log(`ips: ${contract.publicIpsList}`)
}

if (newNodeContract.contractID === BigInt(17661)) {
console.log('contract found')
console.log(touchedIps)
ctx.log.warn(`Can't update IPs for contract ${contractEvent.contractId}`)
ctx.log.warn(`ips: ${JSON.stringify(contract.publicIpsList.map(ip => { return { ip: ip.ip.toString(), gateway: ip.gateway.toString() } }))}`)
}

await ctx.store.save(touchedIps)
Expand Down
71 changes: 66 additions & 5 deletions src/mappings/farms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Ctx } from '../processor'
import * as v63 from '../types/v63'
import { validateString } from "./nodes"

import * as ipaddr from 'ipaddr.js';

export class FarmWithIPs {
constructor(farmID: number, ips: PublicIp[]) {
this.farmID = farmID
Expand Down Expand Up @@ -56,6 +58,10 @@ export async function farmStored(
await ctx.store.save<Farm>(newFarm)

const ipPromises = farmStoredEventParsed.publicIps.map(ip => {
if (!checkIPs(ctx, ip.ip.toString(), ip.gateway.toString())) {
return Promise.resolve()
}

const newIP = new PublicIp()

newIP.id = item.event.id
Expand All @@ -67,13 +73,56 @@ export async function farmStored(
newIP.farm = newFarm

newFarm.publicIPs?.push(newIP)

ctx.log.debug({ eventName: item.name, ip: newIP.ip }, `Public IP: ${newIP.ip} added with farm id: ${newFarm.farmID}`);
return ctx.store.save<PublicIp>(newIP)
})
await Promise.all(ipPromises)
await ctx.store.save<Farm>(newFarm)
}

function checkIPs(ctx: Ctx, ipv4_a: string, ipv4_b: string): boolean {
try {
// Check if both IP addresses are valid
if (!ipaddr.isValidCIDR(ipv4_a) || !ipaddr.isValid(ipv4_b)) {
ctx.log.warn(`One or both IP addresses are invalid. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}
// Parse the IP addresses
const ip_a = ipaddr.parseCIDR(ipv4_a);
const ip_b = ipaddr.parse(ipv4_b);

// check if both IP addresses are the same
if (ip_a[0] == ip_b) {
ctx.log.warn(`The IP addresses are the same. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}

// Check if both IP addresses are public
if (ip_a[0].range() == 'private' || ip_b.range() == 'private') {
ctx.log.warn(`One or both IP addresses are not public. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}

// Check if both IP addresses are unicast addresses
if (ip_a[0].range() !== 'unicast' || ip_b.range() !== 'unicast') {
ctx.log.warn(`One or both IP addresses are not unicast addresses. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}


// Check if the gateway is in the same subnet as the host
if (!ip_b.match(ip_a)) {
ctx.log.warn(`The gateway is not in the same subnet as the host. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}

return true;
} catch (error: any) {
ctx.log.error(`An error occurred: ${error.message}. Public IP: ${ipv4_a}, Gateway: ${ipv4_b}`);
return false;
}
}

export async function farmUpdated(
ctx: Ctx,
item: EventItem<'TfgridModule.FarmUpdated', { event: { args: true } }>
Expand Down Expand Up @@ -122,8 +171,10 @@ export async function farmUpdated(
savedFarm.certification = certification

let eventPublicIPs = farmUpdatedEventParsed.publicIps

await farmUpdatedEventParsed.publicIps.forEach(async ip => {
farmUpdatedEventParsed.publicIps.forEach(async ip => {
if (!checkIPs(ctx, ip.ip.toString(), ip.gateway.toString())) {
return
}
if (ip.ip.toString().indexOf('\x00') >= 0) {
return
}
Expand All @@ -132,20 +183,24 @@ export async function farmUpdated(
if (savedIP) {
savedIP.ip = validateString(ctx, ip.ip.toString()) // not effective, but for since we already check for \x00
savedIP.gateway = validateString(ctx, ip.gateway.toString())
if (savedIP.farm.id !== savedFarm.id) {
ctx.log.error({ eventName: item.name, ip: ip.ip.toString() }, `PublicIP: ${ip.ip.toString()} already exists on farm: ${savedIP.farm.farmID}, skiped adding it to farm with ID: ${savedFarm.farmID}`);
}
await ctx.store.save<PublicIp>(savedIP)
} else {

const newIP = new PublicIp()
newIP.id = item.event.id
newIP.ip = validateString(ctx, ip.ip.toString())
newIP.gateway = validateString(ctx, ip.gateway.toString())
newIP.contractId = ip.contractId
newIP.farm = savedFarm

await ctx.store.save<PublicIp>(newIP)
if (!savedFarm.publicIPs) {
savedFarm.publicIPs = []
}
savedFarm.publicIPs.push(newIP)
ctx.log.debug({ eventName: item.name, ip: ip.ip.toString() }, `PublicIP: ${ip.ip.toString()} added with farm id: ${savedFarm.farmID}`);
}
})

Expand All @@ -156,14 +211,19 @@ export async function farmUpdated(
if (eventPublicIPs.filter(eventIp => validateString(ctx, eventIp.ip.toString()) === ip.ip).length === 0) {
// IP got removed from farm
await ctx.store.remove<PublicIp>(ip)
// remove ip from savedFarm.publicIPs
// savedFarm.publicIPs = savedFarm.publicIPs.filter(savedIP => savedIP.id !== ip.id)
// TODO: check if we need this code above? or Cascade delete involving here?
ctx.log.debug({ eventName: item.name, ip: ip.ip.toString() }, `PublicIP: ${ip.ip.toString()} in farm: ${savedFarm.farmID} removed from publicIPs`);
}
})

let farm = item.event.args as Farm
if (farm.dedicatedFarm) {
savedFarm.dedicatedFarm = farm.dedicatedFarm
await ctx.store.save<Farm>(savedFarm)
}
await ctx.store.save<Farm>(savedFarm)

}

export async function farmDeleted(
Expand All @@ -176,6 +236,7 @@ export async function farmDeleted(

if (savedFarm) {
await ctx.store.remove(savedFarm)
ctx.log.debug({ eventName: item.name, farmID: savedFarm.farmID }, `Farm: ${savedFarm.farmID} removed from storage`);
}
}

Expand Down
16 changes: 7 additions & 9 deletions src/mappings/tftPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,15 @@ export async function priceStored(
let priceEvent
if (priceStoredEvent.isV9) {
priceEvent = BigDecimal(parseI16F16(new BN(priceStoredEvent.asV9[0], 'le'))) // [Uint8Array, Uint8Array] <- U16F16, AccountId
ctx.log.debug("V9: block number: " + block.height.toString() + ", timestamp: " + timestamp.toString() + ", Price: " + priceEvent.toString() + ", Raw: " + priceStoredEvent.asV9[0])
ctx.log.trace(`V9: block number: ${block.height}, timestamp: ${timestamp}, Price: ${priceEvent}, Raw: ${priceStoredEvent.asV9[0]}`)

} else if (priceStoredEvent.isV49) {
// TODO: fix me U16F16 -> number
priceEvent = BigDecimal(parseI16F16(new BN(priceStoredEvent.asV49, 'le'))) // Uint8Array <-U16F16
ctx.log.debug("V49: block number: " + block.height.toString() + ", timestamp: " + timestamp.toString() + ", Price: " + priceEvent.toString() + ", Raw: " + priceStoredEvent.asV49[0])
ctx.log.trace(`V49: block number: ${block.height}, timestamp: ${timestamp}, Price: ${priceEvent}, Raw: ${priceStoredEvent.asV49}`)

} else if (priceStoredEvent.isV101) {
priceEvent = BigDecimal(priceStoredEvent.asV101/1000) // number <- u32 (milli USD)
ctx.log.debug("V101: block number: " + block.height.toString() + ", timestamp: " + timestamp.toString() + ", Price: " + priceEvent.toString() + ", Raw: " + priceStoredEvent.asV101)

priceEvent = BigDecimal(priceStoredEvent.asV101 / 1000) // number <- u32 (milli USD)
ctx.log.trace(`V101: block number: ${block.height}, timestamp: ${timestamp}, Price: ${priceEvent}, Raw: ${priceStoredEvent.asV101}`)
}

if (!priceEvent) {
Expand All @@ -43,7 +41,7 @@ export async function priceStored(
newPrice.block = block.height
newPrice.timestamp = timestamp
newPrice.newPrice = priceEvent

await ctx.store.save<PriceStored>(newPrice)
}

Expand All @@ -57,7 +55,7 @@ export async function averagePriceStored(

let priceEvent
if (averagePriceStoredEvent.isV105) {
priceEvent = BigDecimal(averagePriceStoredEvent.asV105/1000) // number <- u32 (milli USD)
priceEvent = BigDecimal(averagePriceStoredEvent.asV105 / 1000) // number <- u32 (milli USD)
}

if (!priceEvent) {
Expand All @@ -70,7 +68,7 @@ export async function averagePriceStored(
newPrice.block = block.height
newPrice.timestamp = timestamp
newPrice.newAveragePrice = priceEvent
ctx.log.debug("V49: block number: " + block.height.toString() + ", timestamp: " + timestamp.toString() + ", Average Price: " + priceEvent.toString() + ", Raw: " + averagePriceStoredEvent.asV105)
ctx.log.trace(`V105: block number: ${block.height}, timestamp: ${timestamp}, Average Price: ${priceEvent}, Raw: ${averagePriceStoredEvent.asV105}`);

await ctx.store.save<AveragePriceStored>(newPrice)
}
Loading

0 comments on commit b09a8d3

Please sign in to comment.